Browse Source

[2052] Add implementation and unit tests for dns::LabelSequence::compare()

Mukund Sivaraman 13 years ago
parent
commit
57c61f2f45

+ 3 - 0
src/lib/datasrc/rbtree.h

@@ -1260,6 +1260,9 @@ RBTree<T>::previousNode(RBTreeNodeChain<T>& node_path) const {
             // already, which located the exact node. The rest of the function
             // goes one domain left and returns it for us.
             break;
+        default:
+            // This must not happen as Name::compare() never returns NONE.
+            isc_throw(isc::Unexpected, "Name::compare() returned unexpected result");
     }
 
     // So, the node_path now contains the path to a node we want previous for.

+ 13 - 0
src/lib/dns/labelsequence.cc

@@ -71,6 +71,19 @@ LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
     return (true);
 }
 
+NameComparisonResult
+LabelSequence::compare(const LabelSequence& other,
+                       bool case_sensitive) const {
+    if ((!isAbsolute()) || (!other.isAbsolute())) {
+        return (NameComparisonResult(0, 0, NameComparisonResult::NONE));
+    }
+
+    return (name_.partial_compare(other.name_,
+                                  first_label_,
+                                  other.first_label_,
+                                  case_sensitive));
+}
+
 void
 LabelSequence::stripLeft(size_t i) {
     if (i >= getLabelCount()) {

+ 14 - 2
src/lib/dns/labelsequence.h

@@ -85,10 +85,10 @@ public:
     /// \return The length of the data of the label sequence in octets.
     size_t getDataLength() const;
 
-    /// \brief Compares two label sequences.
+    /// \brief Compares two label sequences for equality.
     ///
     /// Performs a (optionally case-insensitive) comparison between this
-    /// LabelSequence and another LabelSequence.
+    /// LabelSequence and another LabelSequence for equality.
     ///
     /// \param other The LabelSequence to compare with
     /// \param case_sensitive If true, comparison is case-insensitive
@@ -96,6 +96,18 @@ public:
     ///         and contain the same data.
     bool equals(const LabelSequence& other, bool case_sensitive = false) const;
 
+    /// \brief Compares two label sequences.
+    ///
+    /// Performs a (optionally case-insensitive) comparison between this
+    /// LabelSequence and another LabelSequence.
+    ///
+    /// \param other The LabelSequence to compare with
+    /// \param case_sensitive If true, comparison is case-insensitive
+    /// \return a <code>NameComparisonResult</code> object representing the
+    /// comparison result.
+    NameComparisonResult compare(const LabelSequence& other,
+                                 bool case_sensitive = false) const;
+
     /// \brief Remove labels from the front of this LabelSequence
     ///
     /// \note No actual memory is changed, this operation merely updates the

+ 25 - 6
src/lib/dns/name.cc

@@ -507,13 +507,26 @@ Name::toText(bool omit_final_dot) const {
 
 NameComparisonResult
 Name::compare(const Name& other) const {
+    return (partial_compare(other, 0, 0));
+}
+
+NameComparisonResult
+Name::partial_compare(const Name& other,
+                      unsigned int first_label,
+                      unsigned int first_label_other,
+                      bool case_sensitive) const {
     // Determine the relative ordering under the DNSSEC order relation of
     // 'this' and 'other', and also determine the hierarchical relationship
     // of the names.
 
+    if ((first_label > labelcount_) ||
+        (first_label_other > other.labelcount_)) {
+        isc_throw(BadValue, "Bad first label indices were passed");
+    }
+
     unsigned int nlabels = 0;
-    unsigned int l1 = labelcount_;
-    unsigned int l2 = other.labelcount_;
+    int l1 = labelcount_ - first_label;
+    int l2 = other.labelcount_ - first_label_other;
     int ldiff = (int)l1 - (int)l2;
     unsigned int l = (ldiff < 0) ? l1 : l2;
 
@@ -521,8 +534,8 @@ Name::compare(const Name& other) const {
         --l;
         --l1;
         --l2;
-        size_t pos1 = offsets_[l1];
-        size_t pos2 = other.offsets_[l2];
+        size_t pos1 = offsets_[l1 + first_label];
+        size_t pos2 = other.offsets_[l2 + first_label_other];
         unsigned int count1 = ndata_[pos1++];
         unsigned int count2 = other.ndata_[pos2++];
 
@@ -536,11 +549,17 @@ Name::compare(const Name& other) const {
         while (count > 0) {
             unsigned char label1 = ndata_[pos1];
             unsigned char label2 = other.ndata_[pos2];
+            int chdiff;
+
+            if (case_sensitive) {
+                chdiff = (int)label1 - (int)label2;
+            } else {
+                chdiff = (int)maptolower[label1] - (int)maptolower[label2];
+            }
 
-            int chdiff = (int)maptolower[label1] - (int)maptolower[label2];
             if (chdiff != 0) {
                 return (NameComparisonResult(chdiff, nlabels,
-                                         NameComparisonResult::COMMONANCESTOR));
+                                             NameComparisonResult::COMMONANCESTOR));
             }
             --count;
             ++pos1;

+ 25 - 1
src/lib/dns/name.h

@@ -142,7 +142,8 @@ public:
         SUPERDOMAIN = 0,
         SUBDOMAIN = 1,
         EQUAL = 2,
-        COMMONANCESTOR = 3
+        COMMONANCESTOR = 3,
+        NONE = 4
     };
 
     ///
@@ -399,6 +400,29 @@ public:
     /// comparison result.
     NameComparisonResult compare(const Name& other) const;
 
+private:
+    /// \brief Partially compare two <code>Name</code>s.
+    ///
+    /// This method performs a partial comparison of the
+    /// <code>Name</code> and <code>other</code> and returns the result
+    /// in the form of a <code>NameComparisonResult</code> object.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the right-hand operand to compare against.
+    /// \param first_label the leftmost label of <code>Name</code> to
+    /// begin comparing from.
+    /// \param first_label_other the leftmost label of
+    /// <code>other</code> to begin comparing from.
+    /// \param case_sensitive If true, comparison is case-insensitive
+    /// \return a <code>NameComparisonResult</code> object representing the
+    /// comparison result.
+    NameComparisonResult partial_compare(const Name& other,
+                                         unsigned int first_label,
+                                         unsigned int first_label_other,
+                                         bool case_sensitive = false) const;
+
+public:
     /// \brief Return true iff two names are equal.
     ///
     /// Semantically this could be implemented based on the result of the

+ 111 - 0
src/lib/dns/tests/labelsequence_unittest.cc

@@ -125,6 +125,117 @@ TEST_F(LabelSequenceTest, equals_insensitive) {
     EXPECT_FALSE(ls5.equals(ls7));
 }
 
+// Compare tests
+TEST_F(LabelSequenceTest, compare) {
+    // "example.org." and "example.org.", case sensitive
+    NameComparisonResult result = ls1.compare(ls3, true);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "example.org." and "example.ORG.", case sensitive
+    result = ls3.compare(ls5, true);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_EQ(1, result.getCommonLabels());
+
+    // "example.org." and "example.ORG.", case in-sensitive
+    result = ls3.compare(ls5);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    Name na("a.example.org");
+    Name nb("b.example.org");
+    LabelSequence lsa(na);
+    LabelSequence lsb(nb);
+
+    // "a.example.org." and "b.example.org.", case in-sensitive
+    result = lsa.compare(lsb);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "example.org." and "b.example.org.", case in-sensitive
+    lsa.stripLeft(1);
+    result = lsa.compare(lsb);
+    EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN,
+              result.getRelation());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    Name nc("g.f.e.d.c.example.org");
+    LabelSequence lsc(nc);
+
+    // "g.f.e.d.c.example.org." and "b.example.org" (no hierarchy), case
+    // in-sensitive
+    lsb.stripRight(1);
+    result = lsc.compare(lsb);
+    EXPECT_EQ(isc::dns::NameComparisonResult::NONE,
+              result.getRelation());
+    EXPECT_EQ(0, result.getCommonLabels());
+    EXPECT_EQ(0, result.getOrder());
+
+    // "g.f.e.d.c.example.org." and "example.org.", case in-sensitive
+    result = lsc.compare(ls1);
+    EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN,
+              result.getRelation());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "e.d.c.example.org." and "example.org.", case in-sensitive
+    lsc.stripLeft(2);
+    result = lsc.compare(ls1);
+    EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN,
+              result.getRelation());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "example.org." and "example.org.", case in-sensitive
+    lsc.stripLeft(3);
+    result = lsc.compare(ls1);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "." and "example.org.", case in-sensitive
+    lsc.stripLeft(2);
+    result = lsc.compare(ls1);
+    EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN,
+              result.getRelation());
+    EXPECT_EQ(1, result.getCommonLabels());
+
+    Name nd("a.b.c.isc.example.org");
+    LabelSequence lsd(nd);
+    Name ne("w.x.y.isc.EXAMPLE.org");
+    LabelSequence lse(ne);
+
+    // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.",
+    // case sensitive
+    result = lsd.compare(lse, true);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_EQ(2, result.getCommonLabels());
+
+    // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.",
+    // case in-sensitive
+    result = lsd.compare(lse);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_EQ(4, result.getCommonLabels());
+
+    // "isc.example.org." and "isc.EXAMPLE.org.", case sensitive
+    lsd.stripLeft(3);
+    lse.stripLeft(3);
+    result = lsd.compare(lse, true);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_EQ(2, result.getCommonLabels());
+
+    // "isc.example.org." and "isc.EXAMPLE.org.", case in-sensitive
+    result = lsd.compare(lse);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(4, result.getCommonLabels());
+}
+
 void
 getDataCheck(const char* expected_data, size_t expected_len,
              const LabelSequence& ls)