table: add Fib::afterNewNextHop signal

Refs: #4931
Change-Id: I68915b5f5688ad2f62147069fea86956a22672b6
diff --git a/daemon/table/fib-entry.cpp b/daemon/table/fib-entry.cpp
index ff5c125..ade4c9f 100644
--- a/daemon/table/fib-entry.cpp
+++ b/daemon/table/fib-entry.cpp
@@ -48,26 +48,32 @@
   return const_cast<Entry*>(this)->findNextHop(face) != m_nextHops.end();
 }
 
-void
+std::pair<NextHopList::iterator, bool>
 Entry::addOrUpdateNextHop(Face& face, uint64_t cost)
 {
   auto it = this->findNextHop(face);
+  bool isNew = false;
   if (it == m_nextHops.end()) {
     m_nextHops.emplace_back(face);
     it = std::prev(m_nextHops.end());
+    isNew = true;
   }
 
   it->setCost(cost);
   this->sortNextHops();
+
+  return std::make_pair(it, isNew);
 }
 
-void
+bool
 Entry::removeNextHop(const Face& face)
 {
   auto it = this->findNextHop(face);
   if (it != m_nextHops.end()) {
     m_nextHops.erase(it);
+    return true;
   }
+  return false;
 }
 
 void
diff --git a/daemon/table/fib-entry.hpp b/daemon/table/fib-entry.hpp
index 52dfbd8..7460eda 100644
--- a/daemon/table/fib-entry.hpp
+++ b/daemon/table/fib-entry.hpp
@@ -36,6 +36,8 @@
 
 namespace fib {
 
+class Fib;
+
 /** \class nfd::fib::NextHopList
  *  \brief Represents a collection of nexthops.
  *
@@ -79,21 +81,24 @@
   bool
   hasNextHop(const Face& face) const;
 
-  /** \brief adds a NextHop record
+private:
+  /** \brief adds a NextHop record to the entry
    *
-   *  If a NextHop record for \p face already exists, its cost is updated.
+   *  If a NextHop record for \p face already exists in the entry, its cost is set to \p cost.
+   *
+   *  \return the iterator to the new or updated NextHop and a bool indicating whether a new
+   *  NextHop was inserted
    */
-  void
+  std::pair<NextHopList::iterator, bool>
   addOrUpdateNextHop(Face& face, uint64_t cost);
 
   /** \brief removes a NextHop record
    *
    *  If no NextHop record for face exists, do nothing.
    */
-  void
+  bool
   removeNextHop(const Face& face);
 
-private:
   /** \note This method is non-const because mutable iterators are needed by callers.
    */
   NextHopList::iterator
@@ -111,6 +116,7 @@
   name_tree::Entry* m_nameTreeEntry = nullptr;
 
   friend class name_tree::Entry;
+  friend class Fib;
 };
 
 } // namespace fib
diff --git a/daemon/table/fib.cpp b/daemon/table/fib.cpp
index 62938e0..99892e6 100644
--- a/daemon/table/fib.cpp
+++ b/daemon/table/fib.cpp
@@ -133,13 +133,31 @@
 }
 
 void
+Fib::addOrUpdateNextHop(Entry& entry, Face& face, uint64_t cost)
+{
+  NextHopList::iterator it;
+  bool isNew;
+  std::tie(it, isNew) = entry.addOrUpdateNextHop(face, cost);
+
+  if (isNew)
+    this->afterNewNextHop(entry.getPrefix(), *it);
+}
+
+Fib::RemoveNextHopResult
 Fib::removeNextHop(Entry& entry, const Face& face)
 {
-  entry.removeNextHop(face);
+  bool isRemoved = entry.removeNextHop(face);
 
-  if (!entry.hasNextHops()) {
+  if (!isRemoved) {
+    return RemoveNextHopResult::NO_SUCH_NEXTHOP;
+  }
+  else if (!entry.hasNextHops()) {
     name_tree::Entry* nte = m_nameTree.getEntry(entry);
     this->erase(nte, false);
+    return RemoveNextHopResult::FIB_ENTRY_REMOVED;
+  }
+  else {
+    return RemoveNextHopResult::NEXTHOP_REMOVED;
   }
 }
 
diff --git a/daemon/table/fib.hpp b/daemon/table/fib.hpp
index 8543440..f5984aa 100644
--- a/daemon/table/fib.hpp
+++ b/daemon/table/fib.hpp
@@ -103,9 +103,22 @@
   void
   erase(const Entry& entry);
 
-  /** \brief Remove the NextHop record for \p face
+  /** \brief Add a NextHop record
+   *
+   *  If a NextHop record for \p face already exists in \p entry, its cost is set to \p cost.
    */
   void
+  addOrUpdateNextHop(Entry& entry, Face& face, uint64_t cost);
+
+  enum class RemoveNextHopResult {
+    NO_SUCH_NEXTHOP, ///< the nexthop is not found
+    NEXTHOP_REMOVED, ///< the nexthop is removed and the fib entry stays
+    FIB_ENTRY_REMOVED ///< the nexthop is removed and the fib entry is removed
+  };
+
+  /** \brief Remove the NextHop record for \p face from \p entry
+   */
+  RemoveNextHopResult
   removeNextHop(Entry& entry, const Face& face);
 
 public: // enumeration
@@ -132,6 +145,11 @@
     return this->getRange().end();
   }
 
+public: // signal
+  /** \brief signals on Fib entry nexthop creation
+   */
+  signal::Signal<Fib, Name, NextHop> afterNewNextHop;
+
 private:
   /** \tparam K a parameter acceptable to NameTree::findLongestPrefixMatch
    */