[svn] commit: r430 - in /branches/jinmei-dnsrrparams: ./ doc/ src/ src/lib/ src/lib/dns/cpp/ src/lib/dns/cpp/testdata/

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Jan 4 20:48:26 UTC 2010


Author: jinmei
Date: Mon Jan  4 20:48:26 2010
New Revision: 430

Log:
merging the name class change in the trunk into the new working branch

Added:
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/buffer.h
      - copied unchanged from r429, trunk/src/lib/dns/cpp/buffer.h
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/buffer_unittest.cc
      - copied unchanged from r429, trunk/src/lib/dns/cpp/buffer_unittest.cc
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/exceptions.cc
      - copied unchanged from r429, trunk/src/lib/dns/cpp/exceptions.cc
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/exceptions.h
      - copied unchanged from r429, trunk/src/lib/dns/cpp/exceptions.h
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/exceptions_unittest.cc
      - copied unchanged from r429, trunk/src/lib/dns/cpp/exceptions_unittest.cc
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/messagerenderer.cc
      - copied unchanged from r429, trunk/src/lib/dns/cpp/messagerenderer.cc
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/messagerenderer.h
      - copied unchanged from r429, trunk/src/lib/dns/cpp/messagerenderer.h
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/messagerenderer_unittest.cc
      - copied unchanged from r429, trunk/src/lib/dns/cpp/messagerenderer_unittest.cc
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/name_unittest.cc
      - copied unchanged from r429, trunk/src/lib/dns/cpp/name_unittest.cc
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/testdata/
      - copied from r429, trunk/src/lib/dns/cpp/testdata/
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/unittest_util.cc
      - copied unchanged from r429, trunk/src/lib/dns/cpp/unittest_util.cc
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/unittest_util.h
      - copied unchanged from r429, trunk/src/lib/dns/cpp/unittest_util.h
Removed:
    branches/jinmei-dnsrrparams/Makefile.in
    branches/jinmei-dnsrrparams/aclocal.m4
    branches/jinmei-dnsrrparams/config.h.in
    branches/jinmei-dnsrrparams/src/Makefile.in
    branches/jinmei-dnsrrparams/src/lib/Makefile.in
Modified:
    branches/jinmei-dnsrrparams/   (props changed)
    branches/jinmei-dnsrrparams/configure.ac
    branches/jinmei-dnsrrparams/doc/Doxyfile
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/Makefile.am
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.cc
    branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.h

Modified: branches/jinmei-dnsrrparams/configure.ac
==============================================================================
--- branches/jinmei-dnsrrparams/configure.ac (original)
+++ branches/jinmei-dnsrrparams/configure.ac Mon Jan  4 20:48:26 2010
@@ -1,7 +1,7 @@
 #                                               -*- Autoconf -*-
 # Process this file with autoconf to produce a configure script.
 
-AC_PREREQ([2.64])
+AC_PREREQ([2.59])
 AC_INIT(bind, 10.0.0, bind10-bugs at isc.org)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
@@ -10,7 +10,10 @@
 # Checks for programs.
 AC_PROG_CXX
 AC_PROG_CC
+AM_CONDITIONAL(GCC, test "$GCC" = yes)
 AC_PROG_RANLIB
+
+AC_PROG_LIBTOOL
 
 # Checks for libraries.
 
@@ -20,6 +23,18 @@
 AC_HEADER_STDBOOL
 AC_TYPE_SIZE_T
 
+# default compiler warning settings
+if test "X$GCC" = "Xyes"; then
+CXXFLAGS="-g -Wall -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
+fi
+
+# produce PIC unless we disable share libraries.  need this for python bindings.
+if test $enable_shared != "no" -a "X$GCC" = "Xyes"; then
+   CXXFLAGS="$CXXFLAGS -fPIC"
+fi
+
+AC_SUBST(CXXFLAGS)
+
 #
 # Check availablity of gtest, which will be used for unit tests.
 #
@@ -28,9 +43,17 @@
     gtest_path="$withval", gtest_path="no")
 if test "$gtest_path" != "no"
 then
-	GTEST_INCLUDES="-I${gtest_path}/include"
-	GTEST_LDFLAGS="-L${gtest_path}/lib"
-	GTEST_LDADD="-lgtest"
+	if test "$gtest_path" != "yes"
+	then
+		GTEST_INCLUDES="-I${gtest_path}/include"
+		GTEST_LDFLAGS="-L${gtest_path}/lib"
+		GTEST_LDADD="-lgtest"
+	else
+		# todo: check for default paths (/usr and /usr/local?)
+		GTEST_INCLUDE="-I/usr/include"
+		GTEST_LDFLAGS="-L/usr/lib"
+		GTEST_LDADD="-lgtest"
+	fi
 else
 	GTEST_INCLUDES=
 	GTEST_LDFLAGS=

Modified: branches/jinmei-dnsrrparams/doc/Doxyfile
==============================================================================
--- branches/jinmei-dnsrrparams/doc/Doxyfile (original)
+++ branches/jinmei-dnsrrparams/doc/Doxyfile Mon Jan  4 20:48:26 2010
@@ -611,7 +611,7 @@
 # against the file with absolute path, so to exclude all test directories
 # for example use the pattern */test/*
 
-EXCLUDE_PATTERNS       = *unittest.h
+EXCLUDE_PATTERNS       = *unittest*.h
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the

Modified: branches/jinmei-dnsrrparams/src/lib/dns/cpp/Makefile.am
==============================================================================
--- branches/jinmei-dnsrrparams/src/lib/dns/cpp/Makefile.am (original)
+++ branches/jinmei-dnsrrparams/src/lib/dns/cpp/Makefile.am Mon Jan  4 20:48:26 2010
@@ -1,13 +1,17 @@
-lib_LIBRARIES = libdns.a
-libdns_a_SOURCES = name.cc name.h
+lib_LTLIBRARIES = libdns.la
+libdns_la_SOURCES = buffer.h name.cc name.h messagerenderer.h messagerenderer.cc
+libdns_la_SOURCES += exceptions.h exceptions.cc
 
 TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
-run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES = unittest_util.h unittest_util.cc
+run_unittests_SOURCES += buffer_unittest.cc name_unittest.cc
+run_unittests_SOURCES += messagerenderer_unittest.cc exceptions_unittest.cc
+run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(GTEST_LDFLAGS)
-run_unittests_LDADD = ./libdns.a $(GTEST_LDADD)
+run_unittests_LDADD = .libs/libdns.a $(GTEST_LDADD)
 endif
 
 noinst_PROGRAMS = $(TESTS)

Modified: branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.cc
==============================================================================
--- branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.cc (original)
+++ branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.cc Mon Jan  4 20:48:26 2010
@@ -14,421 +14,681 @@
 
 // $Id$
 
-#include <stdexcept>
-
+#include <cctype>
+#include <cassert>
+#include <iterator>
+#include <functional>
+
+#include <algorithm>
+
+#include "buffer.h"
 #include "name.h"
-
-using namespace std;
-using namespace ISC::DNS;
-
-// quick hack exception classes: should be moved to an appropriate place soon.
-class ISCException {};
-class ISCUnexpected : public ISCException {};
-class ISCNoSpace : public ISCException {};
-
-class DNSException {};
-class DNSEmptyLabel : public DNSException {};
-class DNSLabelTooLong : public DNSException {};
-class DNSBadEscape : public DNSException {};
-class DNSBadLabelType : public DNSException {};
-
-
+#include "messagerenderer.h"
+
+using isc::dns::NameComparisonResult;
+using isc::dns::MessageRenderer;
+
+namespace isc {
+namespace dns {
+
+namespace {
+///
+/// These are shortcut arrays for efficient character conversion.
+/// digitvalue converts a digit character to the corresponding integer.
+/// maptolower convert uppercase alphabets to their lowercase counterparts.
+/// A helper class and its only instance will initialize the arrays at startup
+/// time.
+///
+static char digitvalue[256];
+static unsigned char maptolower[256];
+
+class Initializer {
+public:
+    Initializer()
+    {
+        for (unsigned int i = 0; i < 256; i++) {
+            if (i >= '0' && i<= '9') {
+                digitvalue[i] = i - '0';
+            } else {
+                digitvalue[i] = -1;
+            }
+        }
+        for (unsigned int i = 0; i < 256; i++) {
+            if (i >= 'A' && i <= 'Z') {
+                maptolower[i] = i - ('A' - 'a');
+            } else {
+                maptolower[i] = i;
+            }
+        }
+    }
+};
+/// This object is defined only to call its constructor.
+static Initializer initialier; 
+}
+
+namespace {
+///
+/// Textual name parser states.
+///
 typedef enum {
-    ft_init = 0,
-    ft_start,
-    ft_ordinary,
-    ft_initialescape,
-    ft_escape,
-    ft_escdecimal,
-    ft_at
+    ft_init = 0,                // begin of the name
+    ft_start,                   // begin of a label
+    ft_ordinary,                // parsing an ordinary label
+    ft_initialescape,           // just found '\'
+    ft_escape,                  // begin of handling a '\'-escaped sequence
+    ft_escdecimal,              // parsing a '\DDD' octet.
+
+    // Unused at this moment.  We'll revisit this when we support master file
+    // parser where @ is used to mean an origin name.
+    ft_at                  
 } ft_state;
-
-typedef enum {
-    fw_start = 0,
-    fw_ordinary,
-    fw_copy,
-    fw_newcurrent
-} fw_state;
-
-static char digitvalue[256] = {
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
-    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /*64*/
-    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
-    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
-};
-
-static unsigned char maptolower[] = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
-    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
-    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
-    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
-    0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
-    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
-    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
-    0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
-    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
-    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
-    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
-    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
-    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
-    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
-    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
-    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
-    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
-    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
-    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
-    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
-    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
-    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
-    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
-    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
-    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
-    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
-    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
-    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
-};
-
-void
-Name::from_string(const string &namestring)
-{
-    char c;
-    ft_state state;
-    unsigned int value, count, pos, lpos;
-    unsigned int n1, n2, tlen, nrem, nused, digits, labels, tused;
-    bool done;
-
-    offsets_.reserve(128);
-    offsets_[0] = 0;
-
+}
+
+Name::Name(const std::string &namestring, bool downcase)
+{
     //
     // Initialize things to make the compiler happy; they're not required.
     //
-    n1 = 0;
-    n2 = 0;
-    digits = 0;
-    value = 0;
-    count = 0;
+    unsigned int digits = 0;
+    unsigned int value = 0;
+    unsigned int count = 0;
 
     //
     // Set up the state machine.
     //
-    pos = 0;
-    tlen = namestring.length();
-    tused = 0;
-    nrem = 255;
-    nused = 0;
-    labels = 0;
-    done = false;
-    state = ft_init;
-
-    while (nrem > 0 && tlen > 0 && !done) {
-        c = namestring[pos++];
-        tlen--;
-        tused++;
+    std::string::const_iterator s = namestring.begin();
+    std::string::const_iterator send = namestring.end();
+    bool done = false;
+    bool is_root = false;
+    ft_state state = ft_init;
+
+    std::vector<unsigned char> offsets;
+    offsets.reserve(Name::MAX_LABELS);
+    offsets.push_back(0);
+
+    std::string ndata;
+    ndata.reserve(Name::MAX_WIRE);
+
+    // should we refactor this code using, e.g, the state pattern?  Probably
+    // not at this point, as this is based on proved code (derived from BIND9)
+    // and it's less likely that we'll have more variations in the domain name
+    // syntax.  If this ever happens next time, we should consider refactor
+    // the code, rather than adding more states and cases below.
+    while (ndata.size() < Name::MAX_WIRE && s != send && !done) {
+        unsigned char c = *s++;
 
         switch (state) {
         case ft_init:
-            /*
-             * Is this the root name?
-             */
+            //
+            // Is this the root name?
+            //
             if (c == '.') {
-                if (tlen != 0)
-                    throw DNSEmptyLabel();
-                labels++;
-                ndata_.push_back(0);
-                nrem--;
-                nused++;
+                if (s != send) {
+                    dns_throw(EmptyLabel, "non terminating empty label");
+                }
+                is_root = true;
+            } else if (c == '@' && s == send) {
+                // handle a single '@' as the root name.
+                is_root = true;
+            }
+
+            if (is_root) {
+                ndata.push_back(0);
                 done = true;
                 break;
             }
-            if (c == '@' && tlen == 0) {
-                state = ft_at;
-                break;
-            }
-
-            /* FALLTHROUGH */
+
+            // FALLTHROUGH
         case ft_start:
-            ndata_.push_back(0); // dummy data
-            nrem--;
-            lpos = nused;
-            nused++;
+            ndata.push_back(0); // placeholder for the label length field
             count = 0;
             if (c == '\\') {
                 state = ft_initialescape;
                 break;
             }
             state = ft_ordinary;
-            if (nrem == 0)
-                throw ISCNoSpace();
-            /* FALLTHROUGH */
+            assert(ndata.size() < Name::MAX_WIRE);
+            // FALLTHROUGH
         case ft_ordinary:
             if (c == '.') {
-                if (count == 0)
-                    throw DNSEmptyLabel();
-                ndata_[lpos] = count;
-                labels++;
-                //INSIST(labels <= 127);
-                offsets_[labels] = nused;
-                if (tlen == 0) {
-                    labels++;
-                    ndata_.push_back(0);
-                    nrem--;
-                    nused++;
+                if (count == 0) {
+                    dns_throw(EmptyLabel, "duplicate period");
+                }
+                ndata.at(offsets.back()) = count;
+                offsets.push_back(ndata.size());
+                if (s == send) {
+                    ndata.push_back(0);
                     done = true;
                 }
                 state = ft_start;
             } else if (c == '\\') {
                 state = ft_escape;
             } else {
-                if (count >= 63)
-                    throw DNSLabelTooLong();
-                count++;
-                ndata_.push_back(c);
-                nrem--;
-                nused++;
+                if (++count > MAX_LABELLEN) {
+                    dns_throw(TooLongLabel, "label is too long");
+                }
+                ndata.push_back(downcase ? maptolower[c] : c);
             }
             break;
         case ft_initialescape:
             if (c == '[') {
-                /*
-                 * This looks like a bitstring label, which
-                 * was deprecated.  Intentionally drop it.
-                 */
-                throw DNSBadLabelType();
+                // This looks like a bitstring label, which was deprecated.
+                // Intentionally drop it.
+                dns_throw(BadLabelType, "invalid label type");
             }
             state = ft_escape;
-            /* FALLTHROUGH */
+            // FALLTHROUGH
         case ft_escape:
             if (!isdigit(c & 0xff)) {
-                if (count >= 63)
-                    throw DNSLabelTooLong();
-                count++;
-                ndata_.push_back(c);
-                nrem--;
-                nused++;
+                if (++count > MAX_LABELLEN) {
+                    dns_throw(TooLongLabel, "label is too long");
+                }
+                ndata.push_back(downcase ? maptolower[c] : c);
                 state = ft_ordinary;
                 break;
             }
             digits = 0;
             value = 0;
             state = ft_escdecimal;
-            /* FALLTHROUGH */
+            // FALLTHROUGH
         case ft_escdecimal:
-            if (!isdigit(c & 0xff))
-                throw DNSBadEscape();
+            if (!isdigit(c & 0xff)) {
+                dns_throw(BadEscape, "mixture of escaped digit and non-digit");
+            }
             value *= 10;
-            value += digitvalue[(int)c];
+            value += digitvalue[c];
             digits++;
             if (digits == 3) {
-                if (value > 255)
-                    throw DNSBadEscape();
-                if (count >= 63)
-                    throw DNSLabelTooLong();
-                count++;
-                ndata_.push_back(value);
-                nrem--;
-                nused++;
+                if (value > 255) {
+                    dns_throw(BadEscape, "escaped decimal is too large");
+                }
+                if (++count > MAX_LABELLEN) {
+                    dns_throw(TooLongLabel, "label is too long");
+                }
+                ndata.push_back(downcase ? maptolower[value] : value);
                 state = ft_ordinary;
             }
             break;
         default:
-            throw runtime_error("Unexpected state " + state);
+            // impossible case
+            assert(false);
+        }
+    }
+
+    if (!done) {                // no trailing '.' was found.
+        if (ndata.size() == Name::MAX_WIRE) {
+            dns_throw(TooLongName, "name is too long for termination");
+        }
+        assert(s == send);
+        if (state != ft_ordinary && state != ft_at) {
+            dns_throw(IncompleteName, "incomplete textual name");
+        }
+        if (state == ft_ordinary) {
+            assert(count != 0);
+            ndata.at(offsets.back()) = count;
+            
+            offsets.push_back(ndata.size());
+            // add a trailing \0
+            ndata.push_back('\0');
+        }
+    }
+
+    labelcount_ = offsets.size();
+    assert(labelcount_ > 0 && labelcount_ <= Name::MAX_LABELS);
+    ndata_.assign(ndata.data(), ndata.size());
+    length_ = ndata_.size();
+    offsets_.assign(offsets.begin(), offsets.end());
+}
+
+namespace {
+///
+/// Wire-format name parser states.
+///
+typedef enum {
+    fw_start = 0,               // beginning of a label
+    fw_ordinary,                // inside an ordinary (non compressed) label
+    fw_newcurrent               // beginning of a compression pointer
+} fw_state;
+}
+
+Name::Name(InputBuffer& buffer, bool downcase)
+{
+    std::vector<unsigned char> offsets;
+    offsets.reserve(Name::MAX_LABELS);
+
+    /*
+     * Initialize things to make the compiler happy; they're not required.
+     */
+    unsigned int n = 0;
+
+    //
+    // Set up.
+    //
+    bool done = false;
+    unsigned int nused = 0;
+    bool seen_pointer = false;
+    fw_state state = fw_start;
+
+    unsigned int cused = 0;     // Bytes of compressed name data used
+    unsigned int current = buffer.getPosition();
+    unsigned int pos_begin = current;
+    unsigned int biggest_pointer = current;
+
+    // Make the compiler happy; this is not required.
+    // XXX: bad style in that we initialize it with a dummy value and define
+    // it far from where it's used.  But alternatives seemed even worse.
+    unsigned int new_current = 0;
+
+    //
+    // Note:  The following code is not optimized for speed, but
+    // rather for correctness.  Speed will be addressed in the future.
+    //
+    while (current < buffer.getLength() && !done) {
+        unsigned int c = buffer.readUint8();
+        current++;
+        if (!seen_pointer) {
+            cused++;
+        }
+
+        switch (state) {
+        case fw_start:
+            if (c <= MAX_LABELLEN) {
+                offsets.push_back(nused);
+                if (nused + c + 1 > Name::MAX_WIRE) {
+                    dns_throw(TooLongName, "wire name is too long");
+                }
+                nused += c + 1;
+                ndata_.push_back(c);
+                if (c == 0) {
+                    done = true;
+                }
+                n = c;
+                state = fw_ordinary;
+            } else if ((c & COMPRESS_POINTER_MARK8) == COMPRESS_POINTER_MARK8) {
+                //
+                // Ordinary 14-bit pointer.
+                //
+                new_current = c & ~COMPRESS_POINTER_MARK8;
+                n = 1;
+                state = fw_newcurrent;
+            } else {
+                // this case includes local compression pointer, which hasn't
+                // been standardized.
+                dns_throw(BadLabelType, "unknown label character");
+            }
+            break;
+        case fw_ordinary:
+            if (downcase) {
+                c = maptolower[c];
+            }
+            ndata_.push_back(c);
+            if (--n == 0) {
+                state = fw_start;
+            }
+            break;
+        case fw_newcurrent:
+            new_current *= 256;
+            new_current += c;
+            if (--n != 0) {
+                break;
+            }
+            if (new_current >= biggest_pointer) {
+                dns_throw(BadPointer, "bad compression pointer: out of range");
+            }
+            biggest_pointer = new_current;
+            current = new_current;
+            buffer.setPosition(current);
+            seen_pointer = true;
+            state = fw_start;
+            break;
+        default:
+            assert(false);
         }
     }
 
     if (!done) {
-        if (nrem == 0)
-            throw ISCNoSpace();
-        //INSIST(tlen == 0);
-        if (state != ft_ordinary && state != ft_at)
-            throw runtime_error("Unexpected state " + state);
-        if (state == ft_ordinary) {
-            //INSIST(count != 0);
-            ndata_[lpos] = count;
-            labels++;
-            //INSIST(labels <= 127);
-            offsets_[labels] = nused;
-
-            // added a trailing \0
-            ndata_.push_back('\0');
-            ++labels;
-            ++nused;
-            offsets_[labels] = nused;
-        }
-    }
-
-    labels_ = labels;
+        dns_throw(IncompleteName, "incomplete wire-format name");
+    }
+
+    labelcount_ = offsets.size();
     length_ = nused;
-}
-
-string
-Name::to_text(bool omit_final_dot) const
-{
-    string tdata;
-    unsigned int nlen;
-    unsigned char c;
-    unsigned int count;
-    unsigned int labels;
-    bool saw_root = false;
-    string::const_iterator iter_ndata;
-
-    /*
-     * This function assumes the name is in proper uncompressed
-     * wire format.
-     */
-    iter_ndata = ndata_.begin();
-    nlen = length_;
-    labels = labels_;
-
-    if (labels == 0 && nlen == 0) {
-        /*
-         * Special handling for an empty name.
-         */
-
-        /*
-         * The names of these booleans are misleading in this case.
-         * This empty name is not necessarily from the root node of
-         * the DNS root zone, nor is a final dot going to be included.
-         * They need to be set this way, though, to keep the "@"
-         * from being trounced.
-         */
-        saw_root = true;
-        omit_final_dot = false;
-        tdata.push_back('@');
-
-        /*
-         * Skip the while() loop.
-         */
-        nlen = 0;
-    } else if (nlen == 1 && labels == 1 && *iter_ndata == '\0') {
-        /*
-         * Special handling for the root label.
-         */
-        saw_root = true;
-        omit_final_dot = false;
-        tdata.push_back('.');
-
-        /*
-         * Skip the while() loop.
-         */
-        nlen = 0;
-    }
-
-    while (labels > 0 && nlen > 0) {
+    offsets_.assign(offsets.begin(), offsets.end());
+    buffer.setPosition(pos_begin + cused);
+}
+
+void
+Name::toWire(OutputBuffer& buffer) const
+{
+    buffer.writeData(ndata_.data(), ndata_.size());
+}
+
+void
+Name::toWire(MessageRenderer& renderer) const
+{
+    renderer.writeName(*this);
+}
+
+std::string
+Name::toText(bool omit_final_dot) const
+{
+    if (length_ == 1) {
+        //
+        // Special handling for the root label.  We ignore omit_final_dot.
+        //
+        assert(labelcount_ == 1 && ndata_[0] == '\0');
+        return (".");
+    }
+
+    std::string::const_iterator np = ndata_.begin();
+    std::string::const_iterator np_end = ndata_.end();
+    unsigned int labels = labelcount_; // use for integrity check
+    // init with an impossible value to catch error cases in the end:
+    unsigned int count = MAX_LABELLEN + 1;
+
+    // result string: it will roughly have the same length as the wire format
+    // name data.  reserve that length to minimize reallocation.
+    std::string result;
+    result.reserve(length_);
+
+    while (np != np_end) {
         labels--;
-        count = *iter_ndata++;
-        nlen--;
+        count = *np++;
+
         if (count == 0) {
-            saw_root = true;
+            if (!omit_final_dot) {
+                result.push_back('.');
+            }
             break;
         }
-        if (count < 64) {
-            //INSIST(nlen >= count);
-            while (count > 0) {
-                c = *iter_ndata;
+            
+        if (count <= MAX_LABELLEN) {
+            assert(np_end - np >= count);
+
+            if (!result.empty()) {
+                // just after a non-empty label.  add a separating dot.
+                result.push_back('.');
+            }
+
+            while (count-- > 0) {
+                unsigned char c = *np++;
                 switch (c) {
-                case 0x22: /* '"' */
-                case 0x28: /* '(' */
-                case 0x29: /* ')' */
-                case 0x2E: /* '.' */
-                case 0x3B: /* ';' */
-                case 0x5C: /* '\\' */
-                    /* Special modifiers in zone files. */
-                case 0x40: /* '@' */
-                case 0x24: /* '$' */
-                    tdata.push_back('\\');
-                    tdata.push_back(c);
-                    iter_ndata++;
-                    nlen--;
+                case 0x22: // '"'
+                case 0x28: // '('
+                case 0x29: // ')'
+                case 0x2E: // '.'
+                case 0x3B: // ';'
+                case 0x5C: // '\\'
+                    // Special modifiers in zone files.
+                case 0x40: // '@'
+                case 0x24: // '$'
+                    result.push_back('\\');
+                    result.push_back(c);
                     break;
                 default:
                     if (c > 0x20 && c < 0x7f) {
-                        tdata.push_back(c);
-                        iter_ndata++;
-                        nlen--;
+                        // append printable characters intact
+                        result.push_back(c);
                     } else {
-                        tdata.push_back(0x5c);
-                        tdata.push_back(0x30 + ((c / 100) % 10));
-                        tdata.push_back(0x30 + ((c / 10) % 10));
-                        tdata.push_back(0x30 + (c % 10));
-                        iter_ndata++;
-                        nlen--;
+                        // encode non-printable characters in the form of \DDD
+                        result.push_back(0x5c);
+                        result.push_back(0x30 + ((c / 100) % 10));
+                        result.push_back(0x30 + ((c / 10) % 10));
+                        result.push_back(0x30 + (c % 10));
                     }
                 }
-                count--;
-            }
-        } else
-            throw runtime_error("Unexpected label type " + count);
-        // The following assumes names are absolute.  If not, we
-        // fix things up later.  Note that this means that in some
-        // cases one more byte of text buffer is required than is
-        // needed in the final output.
-        tdata.push_back('.');
-    }
-
-    if (nlen != 0)
-        throw ISCNoSpace();
-
-    if (!saw_root || omit_final_dot)
-        tdata.erase(tdata.end() - 1);
-
-    return (tdata);
-}
-
-// Are 'this' name and 'other' equal?
+            }
+        } else {
+            dns_throw(BadLabelType, "unknown label type in name data");
+        }
+    }
+
+    assert(labels == 0);
+    assert(count == 0);         // a valid name must end with a 'dot'.
+
+    return (result);
+}
+
+NameComparisonResult
+Name::compare(const Name& other) const
+{
+    // Determine the relative ordering under the DNSSEC order relation of
+    // 'this' and 'other', and also determine the hierarchical relationship
+    // of the names.
+
+    unsigned int nlabels = 0;
+    unsigned int l1 = labelcount_;
+    unsigned int l2 = other.labelcount_;
+    int ldiff = (int)l1 - (int)l2;
+    unsigned int l = (ldiff < 0) ? l1 : l2;
+
+    while (l > 0) {
+        --l;
+        --l1;
+        --l2;
+        size_t pos1 = offsets_[l1];
+        size_t pos2 = other.offsets_[l2];
+        unsigned int count1 = ndata_[pos1++];
+        unsigned int count2 = other.ndata_[pos2++];
+
+        // We don't support any extended label types including now-obsolete
+        // bitstring labels.
+        assert(count1 <= MAX_LABELLEN && count2 <= MAX_LABELLEN);
+
+        int cdiff = (int)count1 - (int)count2;
+        unsigned int count = (cdiff < 0) ? count1 : count2;
+
+        while (count > 0) {
+            unsigned char label1 = ndata_[pos1];
+            unsigned char label2 = other.ndata_[pos2];
+
+            int chdiff = (int)maptolower[label1] - (int)maptolower[label2];
+            if (chdiff != 0) {
+                return (NameComparisonResult(chdiff, nlabels,
+                                         NameComparisonResult::COMMONANCESTOR));
+            }
+            --count;
+            ++pos1;
+            ++pos2;
+        }
+        if (cdiff != 0) {
+                return (NameComparisonResult(cdiff, nlabels,
+                                         NameComparisonResult::COMMONANCESTOR));
+        }
+        ++nlabels;
+    }
+
+    if (ldiff < 0) {
+        return (NameComparisonResult(ldiff, nlabels,
+                                     NameComparisonResult::SUPERDOMAIN));
+    } else if (ldiff > 0) {
+        return (NameComparisonResult(ldiff, nlabels,
+                                     NameComparisonResult::SUBDOMAIN));
+    }
+
+    return (NameComparisonResult(ldiff, nlabels, NameComparisonResult::EQUAL));
+}
+
 bool
-Name::operator==(const Name& other) const
-{
-    unsigned int l;
-    unsigned char c, count;
-    string::const_iterator label1, label2;
-
-    if (length_ != other.length_)
+Name::equals(const Name& other) const
+{
+    if (length_ != other.length_ || labelcount_ != other.labelcount_) {
         return (false);
-
-    l = labels_;
-
-    if (l != other.labels_)
-        return (false);
-
-    label1 = ndata_.begin();
-    label2 = other.ndata_.begin();
-    while (l > 0) {
-        l--;
-        count = *label1++;
-        if (count != *label2++)
+    }
+
+    for (unsigned int l = labelcount_, pos = 0; l > 0; --l) {
+        unsigned char count = ndata_[pos];
+        if (count != other.ndata_[pos]) {
             return (false);
+        }
+        ++pos;
+
+        while (count-- > 0) {
+            unsigned char label1 = ndata_[pos];
+            unsigned char label2 = other.ndata_[pos];
+
+            if (maptolower[label1] != maptolower[label2]) {
+                return (false);
+            }
+            ++pos;
+        }
+    }
+
+    return (true);
+}
+
+bool
+Name::leq(const Name& other) const
+{
+    return (compare(other).getOrder() <= 0);
+}
+
+bool
+Name::geq(const Name& other) const
+{
+    return (compare(other).getOrder() >= 0);
+}
+
+bool
+Name::lthan(const Name& other) const
+{
+    return (compare(other).getOrder() < 0);
+}
+
+bool
+Name::gthan(const Name& other) const
+{
+    return (compare(other).getOrder() > 0);
+}
+
+bool
+Name::isWildcard() const
+{
+    return (length_ >= 2 && ndata_[0] == 1 && ndata_[1] == '*'); 
+}
+
+namespace {                     // hide the local class
+///
+/// A helper functor class to add an additional offset to an offset vector.
+///
+struct OffsetAdjuster : public std::binary_function<unsigned char,
+                                                    int, unsigned char> {
+    unsigned char operator()(unsigned char ch, int offset) const
+    {
+        return (ch + offset);
+    }
+};
+}
+
+Name
+Name::concatenate(const Name& suffix) const
+{
+    assert(this->length_ > 0 && suffix.length_ > 0);
+    assert(this->labelcount_ > 0 && suffix.labelcount_ > 0);
+
+    unsigned int length = this->length_ + suffix.length_ - 1;
+    if (length > Name::MAX_WIRE) {
+        dns_throw(TooLongName, "names are too long to concatenate");
+    }
+
+    Name retname;
+    retname.ndata_.reserve(length);
+    retname.ndata_.assign(this->ndata_, 0, this->length_ - 1);
+    retname.ndata_.insert(retname.ndata_.end(),
+                          suffix.ndata_.begin(), suffix.ndata_.end());
+    assert(retname.ndata_.size() == length);
+    retname.length_ = length;
+
+    //
+    // Setup the offsets vector.  Copy the offsets of this (prefix) name,
+    // excluding that for the trailing dot, and append the offsets of the
+    // suffix name with the additional offset of the length of the prefix.
+    //
+    unsigned int labels = this->labelcount_ + suffix.labelcount_ - 1;
+    assert(labels <= Name::MAX_LABELS);
+    retname.offsets_.reserve(labels);
+    retname.offsets_.assign(&this->offsets_[0],
+                            &this->offsets_[0] + this->labelcount_ - 1);
+    transform(suffix.offsets_.begin(), suffix.offsets_.end(),
+              back_inserter(retname.offsets_),
+              bind2nd(OffsetAdjuster(), this->length_ - 1));
+    assert(retname.offsets_.size() == labels);
+    retname.labelcount_ = labels;
+
+    return (retname);
+}
+
+Name
+Name::split(unsigned int first, unsigned int n) const
+{
+    if (n == 0 || first + n > labelcount_) {
+        dns_throw(OutOfRange, "Name::split: invalid split range");
+    }
+
+    Name retname;
+    // If the specified range doesn't include the trailing dot, we need one
+    // more label for that.
+    unsigned int newlabels = (first + n == labelcount_) ? n : n + 1;
+
+    //
+    // Set up offsets: copy the corresponding range of the original offsets
+    // with subtracting an offset of the prefix length.
+    //
+    retname.offsets_.reserve(newlabels);
+    transform(offsets_.begin() + first, offsets_.begin() + first + newlabels,
+              back_inserter(retname.offsets_),
+              bind2nd(OffsetAdjuster(), -offsets_[first]));
+
+    //
+    // Set up the new name.  At this point the tail of the new offsets specifies
+    // the position of the trailing dot, which should be equal to the length of
+    // the extracted portion excluding the dot.  First copy that part from the
+    // original name, and append the trailing dot explicitly.
+    //
+    retname.ndata_.reserve(retname.offsets_.back() + 1);
+    retname.ndata_.assign(ndata_, offsets_[first], retname.offsets_.back());
+    retname.ndata_.push_back(0);
+
+    retname.length_ = retname.ndata_.size();
+    retname.labelcount_ = retname.offsets_.size();
+    assert(retname.labelcount_ == newlabels);
+
+    return (retname);
+}
+
+Name&
+Name::downcase()
+{
+    unsigned int nlen = length_;
+    unsigned int labels = labelcount_;
+    unsigned int pos = 0;
+
+    while (labels > 0 && nlen > 0) {
+        --labels;
+        --nlen;
+
+        // we assume a valid name, and do abort() if the assumption fails
+        // rather than throwing an exception.
+        unsigned int count = ndata_.at(pos++);
+        assert(count <= MAX_LABELLEN);
+        assert(nlen >= count);
 
         while (count > 0) {
-            count--;
-            c = maptolower[(unsigned char)*label1++]; // XXX should avoid cast
-            if (c != maptolower[(unsigned char)*label2++])
-                return (false);
-        }
-    }
-
-    return (true);
-}
-
-ostream&
-operator<<(ostream& os, const Name& name)
-{
-    os << name.to_text();
+            ndata_.at(pos) =
+                maptolower[static_cast<unsigned char>(ndata_.at(pos))];
+            ++pos;
+            --nlen;
+            --count;
+        }
+    }
+
+    return (*this);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Name& name)
+{
+    os << name.toText();
     return (os);
 }
+}
+}

Modified: branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.h
==============================================================================
--- branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.h (original)
+++ branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.h Mon Jan  4 20:48:26 2010
@@ -17,81 +17,561 @@
 #ifndef __NAME_H
 #define __NAME_H 1
 
+#include <stdint.h>
+
 #include <string>
 #include <vector>
 
-namespace ISC {
-namespace DNS {
-class Buffer;
-class NameCompressor;
-class NameDecompressor;
-
+#include "exceptions.h"
+
+namespace isc {
+namespace dns {
+class InputBuffer;
+class OutputBuffer;
+class MessageRenderer;
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// encounters an empty label in the middle of a name.
+///
+class EmptyLabel : public Exception {
+public:
+    EmptyLabel(const char* file, size_t line, const char* what) :
+        isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// encounters too long a name.
+///
+class TooLongName : public Exception {
+public:
+    TooLongName(const char* file, size_t line, const char* what) :
+        isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// encounters too long a label.
+///
+class TooLongLabel : public Exception {
+public:
+    TooLongLabel(const char* file, size_t line, const char* what) :
+        isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// encounters an obsolete or incomplete label type.  In effect "obsolete" only
+/// applies to bitstring labels, which would begin with "\[".  Incomplete cases
+/// include an incomplete escaped sequence such as "\12".
+///
+class BadLabelType : public Exception {
+public:
+    BadLabelType(const char* file, size_t line, const char* what) :
+        isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// fails to decode a "\"-escaped sequence.
+///
+class BadEscape : public Exception {
+public:
+    BadEscape(const char* file, size_t line, const char* what) :
+        isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the wire-format
+/// name contains an invalid compression pointer.
+///
+class BadPointer : public Exception {
+public:
+    BadPointer(const char* file, size_t line, const char* what) :
+        isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// finds the input (string or wire-format data) is incomplete.
+///
+/// An attempt of constructing a name from an empty string will trigger this
+/// exception.
+///
+class IncompleteName : public Exception {
+public:
+    IncompleteName(const char* file, size_t line, const char* what) :
+        isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// This is a supplemental class used only as a return value of Name::compare().
+/// It encapsulate a tuple of the comparison: ordering, number of common labels,
+/// and relationship as follows:
+/// - ordering: relative ordering under the DNSSEC order relation
+/// - labels: the number of common significant labels of the two names being
+///   compared
+/// - relationship: see NameComparisonResult::NameRelation
+///
 class NameComparisonResult {
 public:
+    /// The relation of two names under comparison.
+    /// Its semantics for the case of
+    /// <code>name1->compare(name2)</code> (where name1 and name2 are instances
+    /// of the Name class) is as follows:
+    ///    - SUPERDOMAIN: name1 properly contains name2; name2 is a proper
+    ///      subdomain of name1
+    ///    - SUBDOMAIN: name1 is a proper subdomain of name2
+    ///    - EQUAL: name1 and name2 are equal
+    ///    - COMMONANCESTOR: name1 and name2 share a common ancestor
+    ///
+    /// Note that in our implementation there's always a hierarchical
+    /// relationship between any two names since all names are absolute and
+    /// they at least share the trailing empty label.
+    /// So, for example, the relationship between "com." and "net." is
+    /// "commonancestor".  This may be counter intuitive and inconvenient, but
+    /// we'll keep this design at the moment until we decide whether and how to
+    /// handle "non absolute" names (see the description of the \c Name class).
+    /// If we want to (re)introduce the notion of non absolute names, we'll
+    /// want to distinguish "com" and "com.", and the current definition would
+    /// be more compatible for that purpose.
+    /// If, on the other hand, we finally decide we really don't need that
+    /// notion, we'll probably reconsider the design here, too. 
     enum NameRelation {
-        none = 0,
-        contains = 1,
-        subdomain = 2,
-        equal = 3,
-        commonancestor = 4
+        SUPERDOMAIN = 0,
+        SUBDOMAIN = 1,
+        EQUAL = 2,
+        COMMONANCESTOR = 3
     };
 
-    explicit NameComparisonResult(int order, int nlabels,
+    ///
+    /// \name Constructors and Destructor
+    ///
+    //@{
+    /// \brief Constructor from a comparison tuple
+    ///
+    /// This constructor simply initializes the object in the straightforward
+    /// way.
+    explicit NameComparisonResult(int order, unsigned int nlabels,
                                   NameRelation relation) :
         order_(order), nlabels_(nlabels), relation_(relation) {}
-    int get_order() const { return (order_); }
-    int get_common_labels() const { return (nlabels_); }
-    NameRelation get_relation() const { return (relation_); }
+    //@}
+
+    ///
+    /// \name Getter Methods
+    ///
+    //@{
+    /// Returns the ordering of the comparison result
+    int getOrder() const { return (order_); }
+    /// Returns the number of common labels of the comparison result
+    unsigned int getCommonLabels() const { return (nlabels_); }
+    /// Returns the NameRelation of the comparison result
+    NameRelation getRelation() const { return (relation_); }
+    //@}
 private:
     int order_;
-    int nlabels_;
+    unsigned int nlabels_;
     NameRelation relation_;
 };
 
+///
+/// The \c Name class encapsulates DNS names.
+///
+/// It provides interfaces to construct a name from string or wire-format data,
+/// transform a name into a string or wire-format data, compare two names, get
+/// access to various properties of a name, etc.
+///
+/// Notes to developers: Internally, a name object maintains the name data
+/// in wire format as an instance of \c std::string.  Since many string
+/// implementations adopt copy-on-write data sharing, we expect this approach
+/// will make copying a name less expensive in typical cases.  If this is
+/// found to be a significant performance bottleneck later, we may reconsider
+/// the internal representation or perhaps the API.
+///
+/// A name object also maintains a vector of offsets (\c offsets_ member),
+/// each of which is the offset to a label of the name: The n-th element of
+/// the vector specifies the offset to the n-th label.  For example, if the
+/// object represents "www.example.com", the elements of the offsets vector
+/// are 0, 4, 12, and 16.  Note that the offset to the trailing dot (16) is
+/// included.  In the BIND9 DNS library from which this implementation is
+/// derived, the offsets are optional, probably due to performance
+/// considerations (in fact, offsets can always be calculated from the name
+/// data, and in that sense are redundant).  In our implementation, however,
+/// we always build and maintain the offsets.  We believe we need more low
+/// level, specialized data structure and interface where we really need to
+/// pursue performance, and would rather keep this generic API and
+/// implementation simpler.
+///
+/// While many other DNS APIs introduce an "absolute or relative"
+/// attribute of names as defined in RFC1035, names are always "absolute" in
+/// the initial design of this API.
+/// In fact, separating absolute and relative would confuse API users
+/// unnecessarily.  For example, it's not so intuitive to consider the
+/// comparison result of an absolute name with a relative name.
+/// We've looked into how the concept of absolute names is used in BIND9,
+/// and found that in many cases names are generally absolute.
+/// The only reasonable case of separating absolute and relative is in a master
+/// file parser, where a relative name must be a complete name with an "origin"
+/// name, which must be absolute.  So, in this initial design, we chose a
+/// simpler approach: the API generally handles names as absolute; when we
+/// introduce a parser of master files, we'll introduce the notion of relative
+/// names as a special case.
+///
 class Name {
-public:
-    Name() : length_(0), labels_(0) {}
-    explicit Name(const std::string& namestr) : length_(0), labels_(0)
-	{ from_string(namestr); }
-    explicit Name(NameDecompressor& decompressor, Buffer& buffer);
-    // copy constructor (default cp-ctor should work fine)
-    //Name(const Name& orig);
-    // destructor (default dtor should work fine)
-    //~Name();
-
-    std::string to_text(bool omit_final_dot = false) const;
-    void to_wire(Buffer& buffer, NameCompressor& compressor) const;
-    size_t get_length() const { return (length_); }
-    unsigned int get_labels() const { return (labels_); }
+    ///
+    /// \name Constructors and Destructor
+    ///
+    //@{
+private:
+    /// The default constructor
+    ///
+    /// This is used internally in the class implementation, but at least at
+    /// the moment defined as private because it will construct an incomplete
+    /// object in that it doesn't have any labels.  We may reconsider this
+    /// design choice as we see more applications of the class.
+    Name() : length_(0), labelcount_(0) {}
+public:
+    /// Constructor from a string
+    ///
+    /// If the given string does not represent a valid DNS name, an exception
+    /// of class \c EmptyLabel, \c TooLongLabel, \c BadLabelType, \c BadEscape,
+    /// \c TooLongName, or \c IncompleteName will be thrown.
+    /// In addition, if resource allocation for the new name fails, a
+    /// corresponding standard exception will be thrown.
+    ///
+    /// \param namestr A string representation of the name to be constructed.
+    /// \param downcase Whether to convert upper case alphabets to lower case.
+    explicit Name(const std::string& namestr, bool downcase = false);
+    /// Constructor from wire-format data.
+    ///
+    /// The \c buffer parameter normally stores a complete DNS message
+    /// containing the name to be constructed.  The current read position of
+    /// the buffer points to the head of the name.
+    ///
+    /// The input data may or may not be compressed; if it's compressed, this
+    /// method will automatically decompress it.
+    ///
+    /// If the given data does not represent a valid DNS name, an exception
+    /// of class \c TooLongName, \c BadLabelType, \c BadPointer, or
+    /// \c IncompleteName will be thrown.
+    /// In addition, if resource allocation for the new name fails, a
+    /// corresponding standard exception will be thrown.
+    ///
+    /// \param buffer A buffer storing the wire format data.
+    /// \param downcase Whether to convert upper case alphabets to lower case.
+    explicit Name(InputBuffer& buffer, bool downcase = false);
+    //@}
+
+    ///
+    /// \name Getter Methods
+    ///
+    //@{
+    /// \brief Gets the length of the <code>Name</code> in its wire format.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return the length (the number of octets in wire format) of the
+    /// <code>Name</code>
+    size_t getLength() const { return (length_); }
+
+    /// \brief Returns the number of labels contained in the <code>Name</code>.
+    ///
+    /// Note that an empty label (corresponding to a trailing '.') is counted
+    /// as a single label, so the return value of this method must be >0.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return the number of labels
+    unsigned int getLabelCount() const { return (labelcount_); }
+    //@}
+
+    ///
+    /// \name Converter methods
+    ///
+    //@{
+    /// \brief Convert the Name to a string.
+    ///
+    /// This method returns a <code>std::string</code> object representing the
+    /// Name as a string.  Unless <code>omit_final_dot</code> is
+    /// <code>true</code>, the returned string ends with a dot '.'; the default
+    /// is <code>false</code>.  The default value of this parameter is
+    /// <code>true</code>; converted names will have a trailing dot by default.
+    ///
+    /// This function assumes the name is in proper uncompressed wire format.
+    /// If it finds an unexpected label character including compression pointer,
+    /// an exception of class \c BadLabelType will be thrown.
+    /// In addition, if resource allocation for the result string fails, a
+    /// corresponding standard exception will be thrown.
+    //
+    /// \param omit_final_dot whether to omit the trailing dot in the output.
+    /// \return a string representation of the <code>Name</code>.
+    std::string toText(bool omit_final_dot = false) const;
+
+    /// \brief Render the <code>Name</code> in the wire format with compression.
+    ///
+    /// This method dumps the Name in wire format with help of \c renderer,
+    /// which encapsulates output buffer and name compression algorithm to
+    /// render the name.
+    ///
+    /// If resource allocation in rendering process fails, a corresponding
+    /// standard exception will be thrown.
+    ///
+    /// \param renderer DNS message rendering context that encapsulates the
+    /// output buffer and name compression information.
+    void toWire(MessageRenderer& renderer) const;
+
+    /// \brief Render the <code>Name</code> in the wire format without
+    /// compression.
+    ///
+    /// If resource allocation in rendering process fails, a corresponding
+    /// standard exception will be thrown.  This can be avoided by preallocating
+    /// a sufficient size of \c buffer.  Specifically, if
+    /// <code>buffer.getCapacity() - buffer.getLength() >= Name::MAX_WIRE</code>
+    /// then this method should not throw an exception.
+    ///
+    void toWire(OutputBuffer& buffer) const;
+    //@}
+
+    ///
+    /// \name Comparison methods
+    ///
+    //@{
+    /// \brief Compare two <code>Name</code>s.
+    ///
+    /// This method compares the <code>Name</code> and <code>other</code> and
+    /// returns the result in the form of a <code>NameComparisonResult</code>
+    /// object.
+    ///
+    /// Note that this is case-insensitive comparison.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the right-hand operand to compare against.
+    /// \return a <code>NameComparisonResult</code> object representing the
+    /// comparison result.
     NameComparisonResult compare(const Name& other) const;
+
+    /// \brief Return true iff two names are equal.
+    ///
+    /// Semantically this could be implemented based on the result of the
+    /// \c compare() method, but the actual implementation uses different code
+    /// that simply performs character-by-character comparison (case
+    /// insensitive for the name label parts) on the two names.  This is because
+    /// it would be much faster and the simple equality check would be pretty
+    /// common.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the <code>Name</code> object to compare against.
+    /// \return true if the two names are equal; otherwise false.
+    bool equals(const Name& other) const;
+
+    /// Same as equals()
+    bool operator==(const Name& other) const { return (this->equals(other)); }
+
+    /// \brief Return true iff two names are not equal.
+    ///
+    /// This method simply negates the result of \c equal() method, and in that
+    /// sense it's redundant.  The separate method is provided just for
+    /// convenience.
+    bool nequals(const Name& other) const { return !(this->equals(other)); }
+
+    /// Same as nequals()
+    bool operator!=(const Name& other) const { return (this->nequals(other)); }
+
+    /// \brief Less-than or equal comparison for Name against <code>other</code>
+    ///
+    /// The comparison is based on the result of the \c compare() method.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the <code>Name</code> object to compare against.
+    /// \return true if <code>compare(other).getOrder() <= 0</code>;
+    /// otherwise false.
+    bool leq(const Name& other) const;
+
+    /// Same as leq()
+    bool operator<=(const Name& other) const { return (leq(other)); }
+
+    /// \brief Greater-than or equal comparison for Name against
+    /// <code>other</code>
+    ///
+    /// The comparison is based on the result of the \c compare() method.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the <code>Name</code> object to compare against.
+    /// \return true if <code>compare(other).getOrder() >= 0</code>;
+    /// otherwise false.
+    bool geq(const Name& other) const;
+
+    /// Same as geq()
+    bool operator>=(const Name& other) const { return (geq(other)); }
+
+    /// \brief Less-than comparison for Name against <code>other</code>
+    ///
+    /// The comparison is based on the result of the \c compare() method.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the <code>Name</code> object to compare against.
+    /// \return true if <code>compare(other).getOrder() < 0</code>;
+    /// otherwise false.
+    bool lthan(const Name& other) const;
+
+    /// Same as lthan()
+    bool operator<(const Name& other) const { return (lthan(other)); }
+
+    /// \brief Greater-than comparison for Name against <code>other</code>
+    ///
+    /// The comparison is based on the result of the \c compare() method.
+    ////
+    /// This method never throws an exception.
+    ///
+    /// \param other the <code>Name</code> object to compare against.
+    /// \return true if <code>compare(other).getOrder() > 0</code>;
+    /// otherwise false.
+    bool gthan(const Name& other) const;
+
+    /// Same as gthan()
+    bool operator>(const Name& other) const { return (gthan(other)); }
+    //@}
+
+    ///
+    /// \name Transformer methods
+    ///
+    //@{
+    /// \brief Extract a specified subpart of Name.
+    ///
+    /// <code>name.split(first, n)</code> constructs a new name starting from
+    /// the <code>first</code>-th label of the \c name, and subsequent \c n
+    /// labels including the \c first one.  Since names in this current
+    /// implementation are always "absolute", if the specified range doesn't
+    /// contain the trailing dot of the original \c name, then a dot will be
+    /// appended to the resulting name.  As a result, the number of labels
+    /// will be <code>n + 1</code>, rather than \c n.  For example,
+    /// when \c n is <code>Name("www.example.com")</code>,
+    /// both <code>n.split(1, 2)</code> and <code>n.split(1, 3)</code>
+    /// will produce a name corresponding to "example.com.", which has 3 labels.
+    /// Note also that labels are counted from 0, and so <code>first = 1</code>
+    /// in this example specified the label "example", not "www".
+    ///
+    /// Parameter \c n must be larger than 0, and the range specified by
+    /// \c first and \c n must not exceed the valid range of the original name;
+    /// otherwise, an exception of class \c OutOfRange will be thrown.
+    ///
+    /// Note to developers: we may want to have different versions (signatures)
+    /// of this method.  For example, we want to split the Name based on a given
+    /// suffix name.
+    ///
+    /// \param first The start position (in labels) of the extracted name
+    /// \param n Number of labels of the extracted name
+    /// \return A new Name object based on the Name containing <code>n</code>
+    /// labels including and following the <code>first</code> label.  
     Name split(unsigned int first, unsigned int n) const;
+
+    /// \brief Concatenate two names.
+    ///
+    /// This method appends \c suffix to \c this Name.  The trailing dot of
+    /// \c this Name will be removed.  For example, if \c this is "www."
+    /// and \c suffix is "example.com.", a successful return of this method
+    /// will be a name of "www.example.com."
+    ///
+    ///The resulting length of the concatenated name must not exceed
+    /// \c Name::MAX_WIRE; otherwise an exception of class
+    /// \c TooLongName will be thrown.
+    ///
+    /// \param suffix a Name object to be appended to the Name.
+    /// \return a new Name object concatenating \c suffix to \c this Name.
     Name concatenate(const Name& suffix) const;
-    bool is_wildcard() const;
-    bool operator==(const Name& other) const;
-    bool equals(const Name& other) const; // alias of ==
-    bool operator!=(const Name& other) const { return (!(*this == other)); }
-    bool nequals(const Name& other) const; // alias of !=
-    bool operator<=(const Name& other) const;
-    bool leq(const Name& other) const; // alias of <=
-    bool operator>=(const Name& other) const;
-    bool geq(const Name& other) const; // alias of >=
-    bool operator<(const Name& other) const;
-    bool lthan(const Name& other) const; // alias of <
-    bool operator>(const Name& other) const;
-    bool gthan(const Name& other) const; // alias of >
+
+    /// \brief Downcase all upper case alphabet characters in the name.
+    ///
+    /// This method modifies the calling object so that it can perform the
+    /// conversion as fast as possible and can be exception free.
+    ///
+    /// The return value of this version of \c downcase() is a reference to
+    /// the calling object (i.e., \c *this) so that the caller can use the
+    /// result of downcasing in a single line.  For example, if variable
+    /// \c n is a \c Name class object possibly containing upper case
+    /// characters, and \c b is an \c OutputBuffer class object, then the
+    /// following code will dump the name in wire format to \c b with
+    /// downcasing upper case characters:
+    ///
+    /// \code n.downcase().toWire(b); \endcode
+    ///
+    /// Since this method modifies the calling object, a \c const name object
+    /// cannot call it.  If \c n is a \c const Name class object, it must first
+    /// be copied to a different object and the latter must be used for the
+    /// downcase modification.
+    ///
+    /// \return A reference to the calling object with being downcased.
+    Name& downcase();
+    //@}
+
+    ///
+    /// \name Testing methods
+    ///
+    //@{
+    /// \brief Test if this is a wildcard name.
+    ///
+    /// \return \c true if the least significant label of this Name is
+    /// <code>'*'</code>; otherwise \c false.
+    bool isWildcard() const;
+    //@}
+
+    ///
+    /// \name Protocol constants
+    ///
+    //@{
+    /// \brief Max allowable length of domain names.
+    static const size_t MAX_WIRE = 255;
+
+    /// \brief Max allowable labels of domain names.
+    ///
+    /// This is <code>ceil(MAX_WIRE / 2)</code>, and is equal to the number of
+    /// labels of name "a.a.a.a....a." (127 "a"'s and trailing dot).
+    static const size_t MAX_LABELS = 128;
+
+    /// \brief Max allowable length of labels of a domain name.
+    static const size_t MAX_LABELLEN = 63;
+
+    /// \brief Max possible pointer value for name compression.
+    ///
+    /// This is the highest number of 14-bit unsigned integer.  Name compression
+    /// pointers are identified as a 2-byte value starting with the upper two
+    /// bit being 11.
+    static const uint16_t MAX_COMPRESS_POINTER = 0x3fff;
+    /// \brief A 8-bit masked value indicating a start of compression pointer.
+    static const uint16_t COMPRESS_POINTER_MARK8 = 0xc0;
+    /// \brief A 16-bit masked value indicating a start of compression pointer.
+    static const uint16_t COMPRESS_POINTER_MARK16 = 0xc000;
+    //@}
 
 private:
-    static const unsigned int MAXWIRE = 255;
-
     std::string ndata_;
-    std::vector<char> offsets_;
+    std::vector<unsigned char> offsets_;
     unsigned int length_;
-    unsigned int labels_;
-
-    void from_string(const std::string& namestr);
-};
-
-std::ostream& operator<<(std::ostream& os, const Name& name);
+    unsigned int labelcount_;
+};
+
+///
+/// \brief Insert the name as a string into stream.
+///
+/// This method convert the \c name into a string and inserts it into the
+/// output stream \c os.
+///
+/// This function overloads the global operator<< to behave as described in
+/// ostream::operator<< but applied to \c Name objects.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param name The \c Name object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream&
+operator<<(std::ostream& os, const Name& name);
 }
 }
 #endif // __NAME_H




More information about the bind10-changes mailing list