table: prevent duplicates in DeadNonceList

refs #5167

Change-Id: Ia7546f75b844cfa1d857a641b015fbc6e9dfaf3d
diff --git a/daemon/table/dead-nonce-list.cpp b/daemon/table/dead-nonce-list.cpp
index c89e9cc..5226140 100644
--- a/daemon/table/dead-nonce-list.cpp
+++ b/daemon/table/dead-nonce-list.cpp
@@ -91,9 +91,15 @@
 DeadNonceList::add(const Name& name, Interest::Nonce nonce)
 {
   Entry entry = DeadNonceList::makeEntry(name, nonce);
-  m_queue.push_back(entry);
 
-  evictEntries();
+  const auto iter = m_ht.find(entry);
+  if (iter != m_ht.end()) {
+    m_queue.relocate(m_queue.end(), m_index.project<0>(iter));
+  }
+  else {
+    m_queue.push_back(entry);
+    evictEntries();
+  }
 }
 
 DeadNonceList::Entry
diff --git a/tests/daemon/table/dead-nonce-list.t.cpp b/tests/daemon/table/dead-nonce-list.t.cpp
index fb3ab90..b4bcec9 100644
--- a/tests/daemon/table/dead-nonce-list.t.cpp
+++ b/tests/daemon/table/dead-nonce-list.t.cpp
@@ -53,6 +53,49 @@
   BOOST_CHECK_EQUAL(dnl.has(nameB, nonce1), false);
 }
 
+BOOST_AUTO_TEST_CASE(NoDuplicates)
+{
+  Name nameA("ndn:/A");
+  const Interest::Nonce nonce1(0x53b4eaa8);
+  const Interest::Nonce nonce2(0x63b4eaa8);
+  const Interest::Nonce nonce3(0x73b4eaa8);
+  const Interest::Nonce nonce4(0x83b4eaa8);
+  const Interest::Nonce nonce5(0x93b4eaa8);
+
+  DeadNonceList dnl;
+  BOOST_CHECK_EQUAL(dnl.size(), 0);
+
+  dnl.m_capacity = 4;
+  dnl.add(nameA, nonce1);
+  dnl.add(nameA, nonce2);
+  dnl.add(nameA, nonce3);
+  dnl.add(nameA, nonce4);
+  BOOST_CHECK_EQUAL(dnl.size(), 4);
+
+  dnl.add(nameA, nonce1);
+  BOOST_CHECK_EQUAL(dnl.size(), 4);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce1), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce2), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce3), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce4), true);
+
+  dnl.add(nameA, nonce5);
+  BOOST_CHECK_EQUAL(dnl.size(), 4);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce1), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce2), false);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce3), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce4), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce5), true);
+
+  dnl.add(nameA, nonce5);
+  BOOST_CHECK_EQUAL(dnl.size(), 4);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce1), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce2), false);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce3), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce4), true);
+  BOOST_CHECK_EQUAL(dnl.has(nameA, nonce5), true);
+}
+
 BOOST_AUTO_TEST_CASE(MinLifetime)
 {
   BOOST_CHECK_THROW(DeadNonceList(0_ms), std::invalid_argument);