exclude: Provide EqualityComparable concept

Change-Id: Iee9a1738881be159b7944ce618869687f61e61b5
diff --git a/src/exclude.cpp b/src/exclude.cpp
index 202ab91..8e44e1a 100644
--- a/src/exclude.cpp
+++ b/src/exclude.cpp
@@ -24,6 +24,7 @@
 #include "exclude.hpp"
 
 #include <boost/static_assert.hpp>
+#include <boost/type_traits.hpp>
 
 namespace ndn {
 
@@ -93,6 +94,8 @@
 void
 Exclude::wireDecode(const Block& wire)
 {
+  clear();
+
   if (wire.type() != tlv::Exclude)
     throw tlv::Error("Unexpected TLV type when decoding Exclude");
 
@@ -173,6 +176,7 @@
   if (!isExcluded(comp))
     {
       m_exclude.insert(std::make_pair(comp, false));
+      m_wire.reset();
     }
   return *this;
 }
@@ -235,6 +239,7 @@
 
   m_exclude.erase(newTo, newFrom); // remove any intermediate node, since all of the are excluded
 
+  m_wire.reset();
   return *this;
 }
 
@@ -258,6 +263,7 @@
     m_exclude.erase(m_exclude.begin(), newFrom);
   }
 
+  m_wire.reset();
   return *this;
 }
 
@@ -280,4 +286,23 @@
   return os;
 }
 
+std::string
+Exclude::toUri() const
+{
+  std::ostringstream os;
+  os << *this;
+  return os.str();
+}
+
+bool
+Exclude::operator==(const Exclude& other) const
+{
+  if (empty() && other.empty())
+    return true;
+  if (empty() || other.empty())
+    return false;
+
+  return wireEncode() == other.wireEncode();
+}
+
 } // namespace ndn
diff --git a/src/exclude.hpp b/src/exclude.hpp
index 9435f41..1475697 100644
--- a/src/exclude.hpp
+++ b/src/exclude.hpp
@@ -137,6 +137,13 @@
   void
   clear();
 
+public: // EqualityComparable concept
+  bool
+  operator==(const Exclude& other) const;
+
+  bool
+  operator!=(const Exclude& other) const;
+
 public: // low-level exclude element API
   typedef std::map< name::Component, bool /*any*/, std::greater<name::Component> > exclude_type;
 
@@ -222,6 +229,7 @@
 Exclude::clear()
 {
   m_exclude.clear();
+  m_wire.reset();
 }
 
 inline size_t
@@ -254,12 +262,10 @@
   return m_exclude.rend();
 }
 
-inline std::string
-Exclude::toUri() const
+inline bool
+Exclude::operator!=(const Exclude& other) const
 {
-  std::ostringstream os;
-  os << *this;
-  return os.str();
+  return !(*this == other);
 }
 
 } // ndn
diff --git a/tests/unit-tests/test-exclude.cpp b/tests/unit-tests/test-exclude.cpp
index 38a811a..ec7029c 100644
--- a/tests/unit-tests/test-exclude.cpp
+++ b/tests/unit-tests/test-exclude.cpp
@@ -55,6 +55,36 @@
   BOOST_CHECK_EQUAL(e.toUri(), "a,b,c,d,aa,cc");
 }
 
+BOOST_AUTO_TEST_CASE(EqualityComparable)
+{
+  Exclude e1;
+  Exclude e2;
+  BOOST_CHECK_EQUAL(e1, e2);
+
+  e1.excludeOne(name::Component("T"));
+  BOOST_CHECK_NE(e1, e2);
+
+  e2.excludeOne(name::Component("D"));
+  BOOST_CHECK_NE(e1, e2);
+
+  e2.clear();
+  e2.excludeOne(name::Component("T"));
+  BOOST_CHECK_EQUAL(e1, e2);
+
+  e2.clear();
+  const uint8_t EXCLUDE[] = { 0x10, 0x15, 0x13, 0x00, 0x08, 0x01, 0x41, 0x08, 0x01, 0x42,
+                              0x08, 0x01, 0x43, 0x13, 0x00, 0x08, 0x01, 0x44, 0x08, 0x01,
+                              0x45, 0x13, 0x00 };
+  e2.wireDecode(Block(EXCLUDE, sizeof(EXCLUDE)));
+
+  e1.clear();
+  e1.excludeBefore(name::Component("A"));
+  e1.excludeOne(name::Component("B"));
+  e1.excludeRange(name::Component("C"), name::Component("D"));
+  e1.excludeAfter(name::Component("E"));
+  BOOST_CHECK_EQUAL(e1, e2);
+}
+
 BOOST_AUTO_TEST_CASE(Before)
 {
   // based on http://redmine.named-data.net/issues/1158