Commit 457ef261 authored by chenzhengzhang's avatar chenzhengzhang
Browse files
parents 8038f6ef e87e34cd
......@@ -682,6 +682,7 @@ AC_CONFIG_FILES([Makefile
src/lib/dns/tests/testdata/Makefile
src/lib/dns/python/Makefile
src/lib/dns/python/tests/Makefile
src/lib/dns/benchmarks/Makefile
src/lib/exceptions/Makefile
src/lib/exceptions/tests/Makefile
src/lib/datasrc/Makefile
......
......@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
......
......@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
......
......@@ -3,13 +3,13 @@
debug
missingInclude
// This is a template, and should be excluded from the check
unreadVariable:src/lib/dns/rdata/template.cc:59
unreadVariable:src/lib/dns/rdata/template.cc:60
// These three trigger warnings due to the incomplete implementation. This is
// our problem, but we need to suppress the warnings for now.
functionConst:src/lib/cache/resolver_cache.h
functionConst:src/lib/cache/message_cache.h
functionConst:src/lib/cache/rrset_cache.h
// Intentional self assignment tests. Suppress warning about them.
selfAssignment:src/lib/dns/tests/name_unittest.cc:292
selfAssignment:src/lib/dns/tests/rdata_unittest.cc:227
selfAssignment:src/lib/dns/tests/name_unittest.cc:293
selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:104
SUBDIRS = . tests python
SUBDIRS = . tests python benchmarks
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
......@@ -72,6 +72,7 @@ libdns___la_SOURCES += name.h name.cc
libdns___la_SOURCES += opcode.h opcode.cc
libdns___la_SOURCES += rcode.h rcode.cc
libdns___la_SOURCES += rdata.h rdata.cc
libdns___la_SOURCES += rdatafields.h rdatafields.cc
libdns___la_SOURCES += rrclass.cc
libdns___la_SOURCES += rrparamregistry.h
libdns___la_SOURCES += rrset.h rrset.cc
......
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
noinst_PROGRAMS = rdatarender_bench
rdatarender_bench_SOURCES = rdatarender_bench.cc
rdatarender_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
rdatarender_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
rdatarender_bench_LDADD += $(SQLITE_LIBS)
- rdatarender_bench
This is a benchmark for RDATA rendering performance comparing the basic
Rdata objects and RdataField objects. It takes a command line argument
that specifies an input data file. Each line of the data file should
specify a single RDATA with its RR class and type, e.g.
IN A 192.0.2.1
IN NS ns.example.com.
Lines beginning with '#' and empty lines will be ignored. Sample input
files can be found in benchmarkdata/rdatarender_*.
# This is sample input data for rdatarender_bench.
# These are RDATA in the authority and additional sections in a response from
# a root server for a query domain under COM.
IN NS g.gtld-servers.net.
IN NS a.gtld-servers.net.
IN NS k.gtld-servers.net.
IN NS c.gtld-servers.net.
IN NS d.gtld-servers.net.
IN NS m.gtld-servers.net.
IN NS h.gtld-servers.net.
IN NS b.gtld-servers.net.
IN NS j.gtld-servers.net.
IN NS l.gtld-servers.net.
IN NS e.gtld-servers.net.
IN NS f.gtld-servers.net.
IN NS i.gtld-servers.net.
IN A 192.5.6.30
IN A 192.33.14.30
IN A 192.26.92.30
IN A 192.31.80.30
IN A 192.12.94.30
IN A 192.35.51.30
IN A 192.42.93.30
IN A 192.54.112.30
IN A 192.43.172.30
IN A 192.48.79.30
IN A 192.52.178.30
IN A 192.41.162.30
IN A 192.55.83.30
IN AAAA 2001:503:a83e::2:30
IN AAAA 2001:503:231d::2:30
# This is sample input data for rdatarender_bench.
# These are RDATA in the authority section in a response from
# a root server for a non existent query domain (with DNSSEC).
IN SOA a.root-servers.net. nstld.verisign-grs.com. 2010110301 1800 900 604800 86400
IN RRSIG SOA 8 0 86400 20101110000000 20101102230000 40288 . WtvYyX2nIsaqjWqkIG1WHFE5PnJ6eno0KqF6azU/MFJ/t1JpKWQ1P4rA 61rnoq0p252fg7wT4XzEz9UDxmpB5pvF2VApe2w9LvSWxsWIIOg8ue5u e9NAAYdzjd0rsYObQQ6msf7WchyAUbnmrqKvf8/CK6+s1xFihXp5DpYL 6K0=
IN NSEC ac. NS SOA RRSIG NSEC DNSKEY
IN RRSIG NSEC 8 0 86400 20101110000000 20101102230000 40288 . rWfgg4YUDFAjhiUOT+niJy/qbaIbydqoXg5oB/5j//ZjNFy4hqU8DvdM xJr9UybQpEvu7pvmKQ0jRYO98Fw/UTlY5KiKbhVBJ1t8AE93cbU+s5gX d3Q6+wRcFX5MjZyIe+f30llKrYOZHjRyEFALCkLt4XEmr0xsua+ztAFY 65k=
IN NSEC np. NS RRSIG NSEC
IN RRSIG NSEC 8 1 86400 20101110000000 20101102230000 40288 . G32LGynsGA2fyDnesyeCtBCoM3ERMgGS4touDUuoBYW1NrZub76kz5fc z93p8VZfoYWAW7LuC8vJ1jl2sUgBNns4zN4RsfFeopcYjjFnGbGuoZnO NmTU+NKO53Ub7uIcCSeqV+COAaL8XqDfyk1FmVdQvtrBaOW/PWpRahVq 7E8=
# This is sample input data for rdatarender_bench.
# These are RDATA in the authority and additional sections in a response from
# a root server for a query domain under ORG.
IN NS b0.org.afilias-nst.org.
IN NS a2.org.afilias-nst.info.
IN NS a0.org.afilias-nst.info.
IN NS c0.org.afilias-nst.info.
IN NS d0.org.afilias-nst.org.
IN NS b2.org.afilias-nst.org.
IN A 199.19.56.1
IN A 199.249.112.1
IN A 199.19.54.1
IN A 199.249.120.1
IN A 199.19.53.1
IN A 199.19.57.1
IN AAAA 2001:500:e::1
IN AAAA 2001:500:40::1
IN AAAA 2001:500:c::1
IN AAAA 2001:500:48::1
IN AAAA 2001:500:b::1
IN AAAA 2001:500:f::1
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <bench/benchmark.h>
#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdatafields.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
using namespace std;
using namespace isc::bench;
using namespace isc::dns;
using namespace isc::dns::rdata;
using isc::util::OutputBuffer;
namespace {
// This templated benchmark class is constructed with a vector of Rdata-like
// (pointer) objects which should have a "toWire()" method. In its run(),
// it calls toWire() for each element of the vector.
template <typename T>
class RdataRenderBenchMark {
public:
RdataRenderBenchMark(const vector<T>& dataset) :
dataset_(dataset), buffer_(4096), renderer_(buffer_)
{}
unsigned int run() {
typename vector<T>::const_iterator data;
typename vector<T>::const_iterator data_end = dataset_.end();
for (data = dataset_.begin(); data != data_end; ++data) {
renderer_.clear();
(*data)->toWire(renderer_);
}
return (dataset_.size());
}
private:
const vector<T>& dataset_;
OutputBuffer buffer_;
MessageRenderer renderer_;
};
// This supplemental class emulates an RRset like class that internally
// uses RdataFields. On construction it stores RDATA information in the
// form of RdataFields fields. Its toWire() method restores the data as
// an RdataFields object for the rendering.
class RdataFieldsStore {
public:
RdataFieldsStore(ConstRdataPtr rdata) {
const RdataFields fields(*rdata);
spec_size_ = fields.getFieldSpecDataSize();
spec_store_.resize(spec_size_);
void* cp_spec = &spec_store_[0];
memcpy(cp_spec, fields.getFieldSpecData(), spec_store_.size());
spec_ptr_ = cp_spec;
data_length_ = fields.getDataLength();
data_store_.resize(data_length_);
void* cp_data = &data_store_[0];
memcpy(cp_data, fields.getData(), data_store_.size());
// Vector guarantees that the elements are stored in continuous array
// in memory, so this is actually correct by the standard
data_ptr_ = cp_data;
}
void toWire(MessageRenderer& renderer) const {
RdataFields(spec_ptr_, spec_size_,
data_ptr_, data_length_).toWire(renderer);
}
private:
vector<unsigned char> spec_store_;
vector<unsigned char> data_store_;
const void* spec_ptr_;
const void* data_ptr_;
unsigned int spec_size_;
size_t data_length_;
};
// We wouldn't necessarily have to use a shared pointer, but it's easier
// to use pointer-like values to adjust them with the RdataRenderBenchMark
// template.
typedef boost::shared_ptr<const RdataFieldsStore> ConstRdataFieldsStorePtr;
void
readInputFile(const char* const input_file, vector<ConstRdataPtr>& rdata_sets,
vector<ConstRdataFieldsStorePtr>& fields_sets)
{
ifstream ifs;
ifs.open(input_file, ios_base::in);
if ((ifs.rdstate() & istream::failbit) != 0) {
cerr << "Failed to read input file: " << input_file << endl;
exit (1);
}
string line;
unsigned int linenum = 0;
while (getline(ifs, line), !ifs.eof()) {
++linenum;
if (ifs.bad() || ifs.fail()) {
cerr << "Unexpected input at line " << linenum << endl;
exit (1);
}
if (line.empty() || line[0] == '#') {
continue; // skip comment and blank lines
}
istringstream iss(line);
string rrclass_string, rrtype_string;
stringbuf rdatabuf;
iss >> rrclass_string >> rrtype_string >> &rdatabuf;
if (iss.bad() || iss.fail()) {
cerr << "Unexpected input at line " << linenum << endl;
exit (1);
}
ConstRdataPtr rdata = createRdata(RRType(rrtype_string),
RRClass(rrclass_string),
rdatabuf.str());
rdata_sets.push_back(rdata);
fields_sets.push_back(ConstRdataFieldsStorePtr(
new RdataFieldsStore(rdata)));
}
ifs.close();
}
void
usage() {
cerr << "Usage: rdatafields_bench [-n iterations] input_file" << endl;
exit (1);
}
}
int
main(int argc, char* argv[]) {
int ch;
int iteration = 10000;
while ((ch = getopt(argc, argv, "n:")) != -1) {
switch (ch) {
case 'n':
iteration = atoi(optarg);
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
usage();
}
const char* const input_file = argv[0];
vector<ConstRdataPtr> rdata_sets;
vector<ConstRdataFieldsStorePtr> fields_sets;
readInputFile(input_file, rdata_sets, fields_sets);
cout << "Parameters:" << endl;
cout << " Iterations: " << iteration << endl;
cout << " Input File: " << input_file << endl;
typedef RdataRenderBenchMark<ConstRdataPtr> RdataBenchMark;
cout << "Benchmark for rendering with standard Rdata" << endl;
BenchMark<RdataBenchMark>(iteration, RdataBenchMark(rdata_sets));
typedef RdataRenderBenchMark<ConstRdataFieldsStorePtr> FieldsBenchMark;
cout << "Benchmark for rendering with RdataFields" << endl;
BenchMark<FieldsBenchMark>(iteration, FieldsBenchMark(fields_sets));
return (0);
}
......@@ -110,7 +110,7 @@ class OutputBuffer;\n'''
content += line
if re.match('// BEGIN_COMMON_DECLARATIONS', line):
content += '''
class MessageRenderer;\n\n'''
class AbstractMessageRenderer;\n\n'''
if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
content += '''
explicit ''' + type_utxt + '''(const std::string& type_str);
......@@ -118,7 +118,7 @@ class MessageRenderer;\n\n'''
''' + type_utxt + '''(const ''' + type_utxt + '''& other);
virtual std::string toText() const;
virtual void toWire(isc::util::OutputBuffer& buffer) const;
virtual void toWire(MessageRenderer& renderer) const;
virtual void toWire(AbstractMessageRenderer& renderer) const;
virtual int compare(const Rdata& other) const;\n\n'''
rdata_header.close()
return content
......
......@@ -152,12 +152,10 @@ struct MessageRenderer::MessageRendererImpl {
///
/// \param buffer An \c OutputBuffer object to which wire format data is
/// written.
MessageRendererImpl(OutputBuffer& buffer) :
buffer_(buffer), nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
MessageRendererImpl() :
nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
truncated_(false), compress_mode_(MessageRenderer::CASE_INSENSITIVE)
{}
/// The buffer that holds the entire DNS message.
OutputBuffer& buffer_;
/// A local working buffer to convert each given name into wire format.
/// This could be a local variable of the \c writeName() method, but
/// we keep it in the class so that we can reuse it and avoid construction
......@@ -176,26 +174,17 @@ struct MessageRenderer::MessageRendererImpl {
};
MessageRenderer::MessageRenderer(OutputBuffer& buffer) :
impl_(new MessageRendererImpl(buffer))
AbstractMessageRenderer(buffer),
impl_(new MessageRendererImpl)
{}
MessageRenderer::~MessageRenderer() {
delete impl_;
}
void
MessageRenderer::skip(const size_t len) {
impl_->buffer_.skip(len);
}
void
MessageRenderer::trim(const size_t len) {
impl_->buffer_.trim(len);
}
void
MessageRenderer::clear() {
impl_->buffer_.clear();
AbstractMessageRenderer::clear();
impl_->nbuffer_.clear();
impl_->nodeset_.clear();
impl_->msglength_limit_ = 512;
......@@ -203,41 +192,6 @@ MessageRenderer::clear() {
impl_->compress_mode_ = CASE_INSENSITIVE;
}
void
MessageRenderer::writeUint8(const uint8_t data) {
impl_->buffer_.writeUint8(data);
}
void
MessageRenderer::writeUint16(const uint16_t data) {
impl_->buffer_.writeUint16(data);
}
void
MessageRenderer::writeUint16At(const uint16_t data, const size_t pos) {
impl_->buffer_.writeUint16At(data, pos);
}
void
MessageRenderer::writeUint32(const uint32_t data) {
impl_->buffer_.writeUint32(data);
}
void
MessageRenderer::writeData(const void* const data, const size_t len) {
impl_->buffer_.writeData(data, len);
}
const void*
MessageRenderer::getData() const {
return (impl_->buffer_.getData());
}
size_t
MessageRenderer::getLength() const {
return (impl_->buffer_.getLength());
}
size_t
MessageRenderer::getLengthLimit() const {
return (impl_->msglength_limit_);
......@@ -293,15 +247,15 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
}
// Record the current offset before extending the buffer.
const size_t offset = impl_->buffer_.getLength();
const size_t offset = getLength();
// Write uncompress part...
impl_->buffer_.writeData(impl_->nbuffer_.getData(),
compress ? i : impl_->nbuffer_.getLength());
writeData(impl_->nbuffer_.getData(),
compress ? i : impl_->nbuffer_.getLength());
if (compress && n != notfound) {
// ...and compression pointer if available.
uint16_t pointer = (*n).pos_;
pointer |= Name::COMPRESS_POINTER_MARK16;
impl_->buffer_.writeUint16(pointer);
writeUint16(pointer);
}
// Finally, add to the set the newly rendered name and its ancestors that
......@@ -313,11 +267,17 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
if (offset + j > Name::MAX_COMPRESS_POINTER) {
break;
}
impl_->nodeset_.insert(NameCompressNode(*this, impl_->buffer_,
impl_->nodeset_.insert(NameCompressNode(*this, getBuffer(),
offset + j,
impl_->nbuffer_.getLength() -
j));
}
}
void
AbstractMessageRenderer::clear() {
buffer_.clear();
}
}
}
......@@ -15,67 +15,64 @@
#ifndef __MESSAGERENDERER_H
#define __MESSAGERENDERER_H 1
#include <util/buffer.h>
namespace isc {
namespace util {
class OutputBuffer;
}
namespace dns {
// forward declarations
class Name;
/// \brief The \c AbstractMessageRenderer class is an abstract base class
/// that provides common interfaces for rendering a DNS message into a buffer
/// in wire format.
///
/// \brief The \c MessageRenderer class encapsulates implementation details
/// of rendering a DNS message into a buffer in wire format.
/// A specific derived class of \c AbstractMessageRenderer (we call it
/// a renderer class hereafter) is simply responsible for name compression at
/// least in the current design. A renderer class object (conceptually)
/// manages the positions of names rendered in some sort of buffer and uses
/// that information to render subsequent names with compression.
///
/// In effect, it's simply responsible for name compression at least in the
/// current implementation. A \c MessageRenderer class object manages the
/// positions of names rendered in a buffer and uses that information to render
/// subsequent names with compression.
///
/// This class is mainly intended to be used as a helper for a more
/// A renderer class is mainly intended to be used as a helper for a more
/// comprehensive \c Message class internally; normal applications won't have
/// to care about this class.
///
/// A \c MessageRenderer class object is constructed with a \c OutputBuffer
/// object, which is the buffer into which the rendered %data will be written.
/// Normally the buffer is expected to be empty on construction, but it doesn't
/// have to be so; the \c MessageRenderer object will start rendering from the
/// end of the buffer at the time of construction. However, if the
/// pre-existing portion of the buffer contains DNS names, these names won't
/// be considered for name compression.
/// to care about details of this class.
///
/// Once a \c MessageRenderer object is constructed with a buffer, it is
/// generally expected that all rendering operations are performed via the
/// \c MessageRenderer object. If the application modifies the buffer in
/// parallel with the \c MessageRenderer, the result will be undefined.
/// Once a renderer class object is constructed with a buffer, it is
/// generally expected that all rendering operations are performed via that
/// object. If the application modifies the buffer in
/// parallel with the renderer, the result will be undefined.
///
/// Note to developers: we introduced a separate class for name compression
/// because previous benchmark with BIND9 showed compression affects overall
/// response performance very much. By having a separate class dedicated for
/// this purpose, we'll be able to change the internal implementation of name
/// compression in the future without affecting other part of the API and
/// implementation. For the same reason, we adopt the "pimpl" idiom in the
/// class definition (i.e., using a pointer to a \c MessageRendererImpl class,
/// which is defined with the class implementation, not in the header file):
/// we may want to modify the compression implementation without modifying the
/// header file thereby requesting rebuild the package.
/// implementation.
///
/// Furthermore, we may eventually want to allow other developers to develop
/// and use their own compression implementation. Should such a case become
/// realistic, we may want to make the \c MessageRendererImpl class an abstract
/// base class and let concrete derived classes have their own implementations.
/// At the moment we don't the strong need for it, so we rather avoid over
/// abstraction and keep the definition simpler.
class MessageRenderer {
/// In addition, by introducing a class hierarchy from
/// \c AbstractMessageRenderer, we allow an application to use a customized
/// renderer class for specific purposes. For example, a high performance
/// DNS server may want to use an optimized renderer class assuming some
/// specific underlying data representation.
///
/// \note Some functions (like writeUint8) are not virtual. It is because
/// it is hard to imagine any version of message renderer that would
/// do anything else than just putting the data into a buffer, so we
/// provide a default implementation and having them virtual would only
/// hurt the performance with no real gain. If it would happen a different
/// implementation is really needed, we can make them virtual in future.
/// The only one that is virtual is writeName and it's because this
/// function is much more complicated, therefore there's a lot of space
/// for different implementations or behaviours.
class AbstractMessageRenderer {
public:
/// \brief Compression mode constants.
///
/// The \c CompressMode enum type represents the name compression mode
/// for the \c MessageRenderer.
/// for renderer classes.
/// \c CASE_INSENSITIVE means compress names in case-insensitive manner;
/// \c CASE_SENSITIVE means compress names in case-sensitive manner.
/// By default, \c MessageRenderer compresses names in case-insensitive
/// By default, a renderer compresses names in case-insensitive
/// manner.
/// Compression mode can be dynamically modified by the
/// \c setCompressMode() method.
......@@ -83,7 +80,7 @@ public:
/// is not an intended usage. In this case the names already compressed
/// are intact; only names being compressed after the mode change are
/// affected by the change.
/// If the internal \c MessageRenderer is reinitialized by the \c clear()
/// If a renderer class object is reinitialized by the \c clear()
/// method, the compression mode will be reset to the default, which is
/// \c CASE_INSENSITIVE
///
......@@ -96,25 +93,39 @@ public:
CASE_INSENSITIVE, //!< Compress names case-insensitive manner (default)
CASE_SENSITIVE //!< Compress names case-sensitive manner
};
public:
protected:
///
/// \name Constructors and Destructor
//@{
/// \brief Constructor from an output buffer.
///
/// \param buffer An \c OutputBuffer object to which wire format data is
/// written.
MessageRenderer(isc::util::OutputBuffer& buffer);