BREAKING: Change Name LSAs to propagate cost which are applied to remote routes

This is a stopgap change to address a lack of functionality in the NDN testbed.
Current restrictions:
-Cost must be set from NFD readvertisement
-Cost is applied after routing calculations but before FIB insertion; lowest cost
nexthop will be chosen but may not be the optimal path given this cost.

Further work will be required to move the application of cost as part of routing
and to make this functionality more easily accessible.

Refs #5349

Change-Id: I914dc5c2d5d3cb6bfa13f5730df0eae66d115c60
diff --git a/src/name-prefix-list.cpp b/src/name-prefix-list.cpp
index 0323896..8012306 100644
--- a/src/name-prefix-list.cpp
+++ b/src/name-prefix-list.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2023,  The University of Memphis,
+ * Copyright (c) 2014-2025,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -21,6 +21,7 @@
 
 #include "name-prefix-list.hpp"
 #include "common.hpp"
+#include "tlv-nlsr.hpp"
 
 namespace nlsr {
 
@@ -34,10 +35,19 @@
 }
 
 bool
-NamePrefixList::insert(const ndn::Name& name, const std::string& source)
+NamePrefixList::insert(const ndn::Name& name, const std::string& source, double cost)
 {
-  auto& sources = m_namesSources[name];
-  return sources.insert(source).second;
+  auto& soucePrefixInfo = m_namesSources[name];
+  soucePrefixInfo.costObj = PrefixInfo(name, cost);
+  return soucePrefixInfo.sources.insert(source).second;
+}
+
+bool
+NamePrefixList::insert(const PrefixInfo& nameCost)
+{
+  auto& soucePrefixInfo = m_namesSources[nameCost.getName()];
+  soucePrefixInfo.costObj = nameCost;
+  return soucePrefixInfo.sources.insert("").second;
 }
 
 bool
@@ -48,30 +58,48 @@
     return false;
   }
 
-  bool isRemoved = it->second.erase(source);
-  if (it->second.empty()) {
+  bool isRemoved = it->second.sources.erase(source);
+  if (it->second.sources.empty()) {
     m_namesSources.erase(it);
   }
   return isRemoved;
 }
 
+const PrefixInfo&
+NamePrefixList::getPrefixInfoForName(const ndn::Name& name) const
+{
+  auto it = m_namesSources.find(name);
+  BOOST_ASSERT(it != m_namesSources.end());
+  return it->second.costObj;
+}
+
 std::list<ndn::Name>
 NamePrefixList::getNames() const
 {
   std::list<ndn::Name> names;
   for (const auto& [name, sources] : m_namesSources) {
-    names.push_back(name);
+    names.emplace_back(name);
   }
   return names;
 }
 
+std::list<PrefixInfo>
+NamePrefixList::getPrefixInfo() const
+{
+  std::list<PrefixInfo> nameCosts;
+  for (const auto& [name, soucePrefixInfo] : m_namesSources) {
+    nameCosts.emplace_back(name, soucePrefixInfo.costObj.getCost());
+  }
+  return nameCosts;
+}
+
 #ifdef WITH_TESTS
 
 std::set<std::string>
 NamePrefixList::getSources(const ndn::Name& name) const
 {
   if (auto it = m_namesSources.find(name); it != m_namesSources.end()) {
-    return it->second;
+    return it->second.sources;
   }
   return {};
 }
@@ -84,7 +112,7 @@
   os << "Name prefix list: {\n";
   for (const auto& [name, sources] : list.m_namesSources) {
     os << name << "\nSources:\n";
-    for (const auto& source : sources) {
+    for (const auto& source : sources.sources) {
       os << "  " << source << "\n";
     }
   }
@@ -92,4 +120,70 @@
   return os;
 }
 
+template<ndn::encoding::Tag TAG>
+size_t
+PrefixInfo::wireEncode(ndn::EncodingImpl<TAG>& encoder) const
+{
+  size_t totalLength = 0;
+
+  totalLength += prependDoubleBlock(encoder, nlsr::tlv::Cost, m_prefixCost);
+
+  totalLength += m_prefixName.wireEncode(encoder);
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(nlsr::tlv::PrefixInfo);
+
+  return totalLength;
+}
+
+NDN_CXX_DEFINE_WIRE_ENCODE_INSTANTIATIONS(PrefixInfo);
+
+const ndn::Block&
+PrefixInfo::wireEncode() const
+{
+  if (m_wire.hasWire()) {
+    return m_wire;
+  }
+
+  ndn::EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  ndn::EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+
+  return m_wire;
+}
+
+void
+PrefixInfo::wireDecode(const ndn::Block& wire)
+{
+  m_wire = wire;
+
+  if (m_wire.type() != nlsr::tlv::PrefixInfo) {
+    NDN_THROW(Error("PrefixInfo", m_wire.type()));
+  }
+
+  m_wire.parse();
+
+  auto val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == ndn::tlv::Name) {
+    m_prefixName.wireDecode(*val);
+    ++val;
+  }
+  else {
+    NDN_THROW(Error("Missing required Name field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == nlsr::tlv::Cost) {
+    m_prefixCost = ndn::encoding::readDouble(*val);
+    ++val;
+  }
+  else {
+    NDN_THROW(Error("Missing required Cost field"));
+  }
+}
+
 } // namespace nlsr