Browse Source

[1688] Add AbstractRRset::lthan()

Provides ordering needed for adding RRsets into a multiset for
duplicate RRset removal.
Stephen Morris 13 years ago
parent
commit
c71fd9915c
3 changed files with 112 additions and 0 deletions
  1. 29 0
      src/lib/dns/rrset.cc
  2. 22 0
      src/lib/dns/rrset.h
  3. 61 0
      src/lib/dns/tests/rrset_unittest.cc

+ 29 - 0
src/lib/dns/rrset.cc

@@ -123,6 +123,35 @@ AbstractRRset::isSameKind(const AbstractRRset& other) const {
 	  getClass() == other.getClass());
 }
 
+bool
+AbstractRRset::lthan(const AbstractRRset& other) const {
+
+    // Check on type first...
+    const uint16_t my_type = getType().getCode();
+    const uint16_t other_type = other.getType().getCode();
+    if (my_type < other_type) {
+        return (true);
+
+    } else if (my_type == other_type) {
+        // Types equal, so check class
+        const uint16_t my_class = getClass().getCode();
+        const uint16_t other_class = other.getClass().getCode();
+        if (my_class < other_class) {
+            return (true);
+
+        } else if (my_class == other_class) {
+            // Class equal, so check name
+            return (getName().lthan(other.getName()));
+
+        } else {
+            return (false);
+        }
+
+    } else {
+        return (false);
+    }
+}
+
 ostream&
 operator<<(ostream& os, const AbstractRRset& rrset) {
     os << rrset.toText();

+ 22 - 0
src/lib/dns/rrset.h

@@ -483,7 +483,29 @@ public:
     ///              against.
     virtual bool isSameKind(const AbstractRRset& other) const;
 
+    /// \brief Check if one RRset is "less" than another
+    ///
+    /// This method is needed for storing RRsets in STL containers such
+    /// as multisets.  It applies an ordering based on
+    /// - Type
+    /// - Class
+    /// - Name
+    /// (Type and Class are ordered by the values associated with those
+    /// constants.  Name is ordered according to case-insensitive comparison.)
+    ///
+    /// Note that unlike isSameKind, type and class are checked before name.
+    /// This is because with ordering based on A, B and C (in that order), the
+    /// algorithm needs to do two checks on A and B - a "less than" check and a
+    /// check for equality.  It only needs to do a "less than" check on C.
+    /// equality.  It only needs to do one check on C, 
+    ///
+    /// \param other The other AbstractRRset to compare against.
+    ///
+    /// \return true if "this" is less than the given RRset according to
+    ///         the criteria given.
+    virtual bool lthan(const AbstractRRset& other) const;
     //@}
+
 };
 
 /// \brief The \c RdataIterator class is an abstract base class that

+ 61 - 0
src/lib/dns/tests/rrset_unittest.cc

@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <stdexcept>
+#include <iostream>
 
 #include <util/buffer.h>
 #include <dns/messagerenderer.h>
@@ -126,6 +127,66 @@ TEST_F(RRsetTest, isSameKind) {
     EXPECT_FALSE(rrset_w.isSameKind(rrset_p));
 }
 
+// Utility function to create an add an RRset to a vector of RRsets for the
+// "less" test.  It's only purpose is to allow the RRset creation to be
+// written with arguments in an order that reflects the RRset ordering.
+void
+addRRset(std::vector<ConstRRsetPtr>& vec, const RRType& rrtype,
+            const RRClass& rrclass, const char* rrname)
+{
+    vec.push_back(ConstRRsetPtr(new RRset(Name(rrname), rrclass, rrtype,
+                                          RRTTL(3600))));
+}
+
+TEST_F(RRsetTest, lthan) {
+    // Check values of type codes: this effectively documents the expected
+    // order of the rrsets created.
+    ASSERT_EQ(1, RRType::A().getCode());
+    ASSERT_EQ(2, RRType::NS().getCode());
+
+    ASSERT_EQ(1, RRClass::IN().getCode());
+    ASSERT_EQ(3, RRClass::CH().getCode());
+
+    // Create a vector of RRsets in ascending sort order.
+    std::vector<ConstRRsetPtr> rrsets;
+    addRRset(rrsets, RRType::A(),  RRClass::IN(), "alpha.com");
+    addRRset(rrsets, RRType::A(),  RRClass::IN(), "beta.com");
+    addRRset(rrsets, RRType::A(),  RRClass::CH(), "alpha.com");
+    addRRset(rrsets, RRType::A(),  RRClass::CH(), "beta.com");
+    addRRset(rrsets, RRType::NS(), RRClass::IN(), "alpha.com");
+    addRRset(rrsets, RRType::NS(), RRClass::IN(), "beta.com");
+    addRRset(rrsets, RRType::NS(), RRClass::CH(), "alpha.com");
+    addRRset(rrsets, RRType::NS(), RRClass::CH(), "beta.com");
+
+    // ... and do the checks.  The ASSERT_ form is used to avoid a plethora
+    // of messages if there is an error.  And if there is an error, supply
+    // a more informative message.
+    for (int i = 0; i < rrsets.size(); ++i) {
+        // Check that an RRset is not less than itself
+        ostringstream ossi;
+        ossi << "i = ("
+             << rrsets[i]->getType().toText() << ", "
+             << rrsets[i]->getClass().toText() << ", "
+             << rrsets[i]->getName().toText()
+             << ")";
+        ASSERT_FALSE(rrsets[i]->lthan(*rrsets[i])) << ossi.str();
+        for (int j = i + 1; j < rrsets.size(); ++j) {
+            // Check it against the remaining RRsets.
+            ostringstream ossj;
+            ossj << ", j = ("
+                 << rrsets[j]->getType().toText() << ", "
+                 << rrsets[j]->getClass().toText() << ", "
+                 << rrsets[j]->getName().toText()
+                 << ")";
+            ASSERT_TRUE(rrsets[i]->lthan(*rrsets[j]))
+                << ossi.str() << ossj.str();
+            ASSERT_FALSE(rrsets[j]->lthan(*rrsets[i]))
+                << ossi.str() << ossj.str();
+        }
+    }
+}
+
+
 void
 addRdataTestCommon(const RRset& rrset) {
     EXPECT_EQ(2, rrset.getRdataCount());