management: Implementing nrd::PrefixRegOptions for NRD Face management protocol

Also in this commit fixes to nfd::FibManagementOptions data structure.

Change-Id: I1ade7df6a18798d9d0881c66a238b0ea84680623
diff --git a/src/encoding/encoding-buffer.hpp b/src/encoding/encoding-buffer.hpp
index c9abd31..1659c52 100644
--- a/src/encoding/encoding-buffer.hpp
+++ b/src/encoding/encoding-buffer.hpp
@@ -512,6 +512,18 @@
 
 template<bool P>
 inline size_t
+prependByteArrayBlock(EncodingImpl<P>& blk, uint32_t type, const uint8_t* array, size_t arraySize)
+{
+  size_t var_len = blk.prependByteArray(array, arraySize);
+  size_t total_len = var_len;
+  total_len += blk.prependVarNumber(var_len);
+  total_len += blk.prependVarNumber(type);
+
+  return total_len;
+}
+
+template<bool P>
+inline size_t
 prependBooleanBlock(EncodingImpl<P>& blk, uint32_t type)
 {
   size_t total_len = blk.prependVarNumber(0);
diff --git a/src/encoding/tlv-nrd.hpp b/src/encoding/tlv-nrd.hpp
new file mode 100644
index 0000000..6dcbbfd
--- /dev/null
+++ b/src/encoding/tlv-nrd.hpp
@@ -0,0 +1,38 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_TLV_NRD_HPP
+#define NDN_TLV_NRD_HPP
+
+#include "tlv.hpp"
+
+namespace ndn {
+namespace tlv {
+namespace nrd {
+
+enum {
+  PrefixRegOptions = 101,
+  FaceId           = 102,
+  Flags            = 103,
+  Cost             = 104,
+  ExpirationPeriod = 105,
+  StrategyName     = 106,
+  Protocol         = 107,
+};
+
+enum {
+  NDN_FORW_CHILD_INHERIT = 1,
+  NDN_FORW_CAPTURE       = 2,
+};
+
+} // namespace nrd
+} // namespace tlv
+} // namespace ndn
+
+#endif // NDN_TLV_NRD_HPP
diff --git a/src/management/nfd-fib-management-options.hpp b/src/management/nfd-fib-management-options.hpp
index c27e2fb..16d688e 100644
--- a/src/management/nfd-fib-management-options.hpp
+++ b/src/management/nfd-fib-management-options.hpp
@@ -23,8 +23,8 @@
   struct Error : public Tlv::Error { Error(const std::string &what) : Tlv::Error(what) {} };
 
   FibManagementOptions ()
-    : faceId_ (-1)
-    , cost_ (-1)
+    : m_faceId (std::numeric_limits<uint64_t>::max())
+    , m_cost (0)
   {
   }
 
@@ -36,45 +36,59 @@
   const Name& 
   getName () const
   {
-    return name_;
+    return m_name;
   }
   
   FibManagementOptions&
   setName (const Name &name)
   {
-    name_ = name;
-    wire_.reset ();
+    m_name = name;
+    m_wire.reset ();
     return *this;
   }
   
-  int 
+  uint64_t 
   getFaceId () const
   {
-    return faceId_;
+    return m_faceId;
   }
 
   FibManagementOptions&
-  setFaceId (int faceId)
+  setFaceId (uint64_t faceId)
   {
-    faceId_ = faceId;
-    wire_.reset ();
+    m_faceId = faceId;
+    m_wire.reset ();
     return *this;
   }
 
-  int 
+  uint64_t 
   getCost () const
   {
-    return cost_;
+    return m_cost;
   }
 
   FibManagementOptions&
-  setCost (int cost)
+  setCost (uint64_t cost)
   {
-    cost_ = cost;
-    wire_.reset ();
+    m_cost = cost;
+    m_wire.reset ();
     return *this;
   }
 
+  const Name& 
+  getStrategy () const
+  {
+    return m_strategy;
+  }
+
+  FibManagementOptions&
+  setStrategy (const Name& strategy)
+  {
+    m_strategy = strategy;
+    m_wire.reset ();
+    return *this;
+  }
+  
   template<bool T>
   size_t
   wireEncode(EncodingImpl<T> &block) const;
@@ -86,13 +100,12 @@
   wireDecode (const Block &wire);
   
 private:
-  Name name_;
-  int faceId_;
-  int cost_;
-  //Name strategy_;
-  //TODO: implement strategy after its use is properly defined
+  Name m_name;
+  uint64_t m_faceId;
+  uint64_t m_cost;
+  Name m_strategy;
 
-  mutable Block wire_;
+  mutable Block m_wire;
 };
 
 template<bool T>
@@ -100,34 +113,34 @@
 FibManagementOptions::wireEncode(EncodingImpl<T>& blk) const
 {
   size_t total_len = 0;
-  if (cost_ != -1)
+
+  // if (!m_strategy.empty())
+  //   {
+  //     total_len += prependNestedBlock(blk, tlv::nfd::Strategy, m_strategy);
+  //   }
+  
+  if (m_cost != 0)
     {
-      size_t var_len = blk.prependNonNegativeInteger (cost_);
-      total_len += var_len;
-      total_len += blk.prependVarNumber (var_len);
-      total_len += blk.prependVarNumber (tlv::nfd::Cost);
+      total_len += prependNonNegativeIntegerBlock(blk, tlv::nfd::Cost, m_cost);
     }
 
-  if (faceId_ != -1)
+  if (m_faceId != std::numeric_limits<uint64_t>::max())
     {
-      size_t var_len = blk.prependNonNegativeInteger (faceId_);
-      total_len += var_len;
-      total_len += blk.prependVarNumber (var_len);
-      total_len += blk.prependVarNumber (tlv::nfd::FaceId);
+      total_len += prependNonNegativeIntegerBlock(blk, tlv::nfd::FaceId, m_faceId);
     }
 
-  total_len += name_.wireEncode (blk);
+  total_len += m_name.wireEncode(blk);
 
-  total_len += blk.prependVarNumber (total_len);
-  total_len += blk.prependVarNumber (tlv::nfd::FibManagementOptions);
+  total_len += blk.prependVarNumber(total_len);
+  total_len += blk.prependVarNumber(tlv::nfd::FibManagementOptions);
   return total_len;
 }
 
 inline const Block&
 FibManagementOptions::wireEncode () const
 {
-  if (wire_.hasWire ())
-    return wire_;
+  if (m_wire.hasWire ())
+    return m_wire;
 
   EncodingEstimator estimator;
   size_t estimatedSize = wireEncode(estimator);
@@ -135,46 +148,54 @@
   EncodingBuffer buffer(estimatedSize, 0);
   wireEncode(buffer);
 
-  wire_ = buffer.block();
-  return wire_;
+  m_wire = buffer.block();
+  return m_wire;
 }
   
 inline void 
 FibManagementOptions::wireDecode (const Block &wire)
 {
-  name_.clear ();
-  faceId_ = -1;
-  cost_ = -1;
+  m_name.clear();
+  m_faceId = std::numeric_limits<uint64_t>::max();
+  m_cost = 0;
+  m_strategy.clear();
 
-  wire_ = wire;
+  m_wire = wire;
 
-  if (wire_.type() != tlv::nfd::FibManagementOptions)
+  if (m_wire.type() != tlv::nfd::FibManagementOptions)
     throw Error("Requested decoding of FibManagementOptions, but Block is of different type");
   
-  wire_.parse ();
+  m_wire.parse ();
 
   // Name
-  Block::element_const_iterator val = wire_.find(Tlv::Name);
-  if (val != wire_.elements_end())
+  Block::element_const_iterator val = m_wire.find(Tlv::Name);
+  if (val != m_wire.elements_end())
     {
-      name_.wireDecode(*val);
+      m_name.wireDecode(*val);
     }
 
   // FaceID
-  val = wire_.find(tlv::nfd::FaceId);
-  if (val != wire_.elements_end())
+  val = m_wire.find(tlv::nfd::FaceId);
+  if (val != m_wire.elements_end())
     {
-      faceId_ = readNonNegativeInteger(*val);
+      m_faceId = readNonNegativeInteger(*val);
     }
 
   // Cost
-  val = wire_.find(tlv::nfd::Cost);
-  if (val != wire_.elements_end())
+  val = m_wire.find(tlv::nfd::Cost);
+  if (val != m_wire.elements_end())
     {
-      cost_ = readNonNegativeInteger(*val);
+      m_cost = readNonNegativeInteger(*val);
     }
 
-  //TODO: Strategy
+  // Strategy
+  val = m_wire.find(tlv::nfd::Strategy);
+  if (val != m_wire.elements_end())
+    {
+      val->parse();
+      if (!val->elements().empty())
+        m_strategy.wireDecode(*val->elements_begin());
+    }
 }
 
 inline std::ostream&
@@ -183,14 +204,17 @@
   os << "ForwardingEntry(";
   
   // Name
-  os << "Prefix: " << option.getName () << ", ";
+  os << "Prefix: " << option.getName() << ", ";
 
   // FaceID
-  os << "FaceID: " << option.getFaceId () << ", ";
+  os << "FaceID: " << option.getFaceId() << ", ";
 
   // Cost
-  os << "Cost: " << option.getCost ();
+  os << "Cost: " << option.getCost() << ", ";
 
+  // Strategy
+  os << "Strategy: " << option.getStrategy() << ", ";
+  
   os << ")";
   return os;
 }
diff --git a/src/management/nrd-prefix-reg-options.hpp b/src/management/nrd-prefix-reg-options.hpp
new file mode 100644
index 0000000..8bb3f7e
--- /dev/null
+++ b/src/management/nrd-prefix-reg-options.hpp
@@ -0,0 +1,363 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2014 Regents of the University of California.
+ *
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NDN_MANAGEMENT_NRD_PREFIX_REG_OPTIONS_HPP
+#define NDN_MANAGEMENT_NRD_PREFIX_REG_OPTIONS_HPP
+
+#include "../encoding/encoding-buffer.hpp"
+#include "../encoding/tlv-nrd.hpp"
+#include "../name.hpp"
+
+namespace ndn {
+namespace nrd {
+
+const uint64_t INVALID_FACE_ID = std::numeric_limits<uint64_t>::max();
+const uint64_t DEFAULT_COST = 0;
+const uint64_t DEFAULT_FLAGS = tlv::nrd::NDN_FORW_CHILD_INHERIT;
+
+/**
+ * @brief Abstraction for prefix registration options for NRD Prefix registration protocol
+ *
+ * @see http://redmine.named-data.net/projects/nrd/wiki/NRD_Prefix_Registration_protocol
+ */
+class PrefixRegOptions {
+public:
+  struct Error : public Tlv::Error { Error(const std::string &what) : Tlv::Error(what) {} };
+
+  PrefixRegOptions()
+    : m_faceId (INVALID_FACE_ID)
+    , m_flags (DEFAULT_FLAGS)
+    , m_cost (DEFAULT_COST)
+    , m_expirationPeriod (-1)
+  {
+  }
+
+  PrefixRegOptions(const Block& block)
+  {
+    wireDecode(block);
+  }
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+  
+  const Block&
+  wireEncode() const;
+  
+  void 
+  wireDecode(const Block& wire);
+  
+  ////////////////////////////////////////////////////////
+  
+  const Name& 
+  getName() const
+  {
+    return m_name;
+  }
+  
+  PrefixRegOptions&
+  setName(const Name& name)
+  {
+    m_name = name;
+    m_wire.reset();
+    return *this;
+  }
+
+  //
+  
+  int
+  getFaceId() const
+  {
+    return m_faceId;
+  }
+
+  PrefixRegOptions&
+  setFaceId(int faceId)
+  {
+    m_faceId = faceId;
+    m_wire.reset();
+    return *this;
+  }
+
+  //
+  
+  uint64_t 
+  getFlags() const
+  {
+    return m_flags;
+  }
+
+  PrefixRegOptions&
+  setFlags(uint64_t flags)
+  {
+    m_flags = flags;
+    m_wire.reset();
+    return *this;
+  }
+
+  //
+  
+  uint64_t 
+  getCost() const
+  {
+    return m_cost;
+  }
+
+  PrefixRegOptions&
+  setCost(uint64_t cost)
+  {
+    m_cost = cost;
+    m_wire.reset();
+    return *this;
+  }
+
+  //
+  
+  Milliseconds
+  getExpirationPeriod() const
+  {
+    return m_expirationPeriod;
+  }
+
+  PrefixRegOptions&
+  setExpirationPeriod(Milliseconds expirationPeriod)
+  {
+    m_expirationPeriod = expirationPeriod;
+    m_wire.reset();
+    return *this;
+  }
+
+  //
+  
+  const Name& 
+  getStrategy() const
+  {
+    return m_strategy;
+  }
+
+  PrefixRegOptions&
+  setStrategy(const Name& strategy)
+  {
+    m_strategy = strategy;
+    m_wire.reset();
+    return *this;
+  }
+
+  //
+
+  const std::string& 
+  getProtocol() const
+  {
+    return m_protocol;
+  }
+
+  PrefixRegOptions&
+  setProtocol(const std::string& protocol)
+  {
+    m_protocol = protocol;
+    m_wire.reset();
+    return *this;
+  }
+  
+private:
+  Name m_name;
+  uint64_t m_faceId;
+  uint64_t m_flags;
+  uint64_t m_cost;
+  Milliseconds m_expirationPeriod;
+  Name m_strategy;
+  std::string m_protocol;
+  
+  mutable Block m_wire;
+};
+
+template<bool T>
+inline size_t
+PrefixRegOptions::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t total_len = 0;
+
+  // PrefixRegOptions ::= PREFIX-REG-OPTIONS-TYPE TLV-LENGTH
+  //                        Name
+  //                        FaceId?
+  //                        Flags?
+  //                        Cost?    
+  //                        ExpirationPeriod?
+  //                        StrategyName?
+  //                        Protocol?
+
+  // (reverse encoding)
+
+  // Protocol
+  if (!m_protocol.empty())
+    {
+      total_len += prependByteArrayBlock(block,
+                                         tlv::nrd::Protocol,
+                                         reinterpret_cast<const uint8_t*>(m_protocol.c_str()),
+                                         m_protocol.size());
+    }
+
+  // StrategyName
+  if (!m_strategy.empty())
+    {
+      total_len += prependNestedBlock(block, tlv::nrd::StrategyName, m_strategy);
+    }
+
+  // ExpirationPeriod
+  if (m_expirationPeriod > 0)
+    {
+      total_len += prependNonNegativeIntegerBlock(block,
+                                                  tlv::nrd::ExpirationPeriod, m_expirationPeriod);
+    }
+
+  // Cost
+  if (m_cost != DEFAULT_COST)
+    {
+      total_len += prependNonNegativeIntegerBlock(block, tlv::nrd::Cost, m_cost);
+    }
+
+  // Flags
+  if (m_flags != DEFAULT_FLAGS)
+    {
+      total_len += prependNonNegativeIntegerBlock(block, tlv::nrd::Flags, m_flags);
+    }
+
+  // FaceId
+  if (m_faceId != INVALID_FACE_ID)
+    {
+      total_len += prependNonNegativeIntegerBlock(block, tlv::nrd::FaceId, m_faceId);
+    }
+
+  // Name
+  total_len += m_name.wireEncode(block);
+
+  total_len += block.prependVarNumber(total_len);
+  total_len += block.prependVarNumber(tlv::nrd::PrefixRegOptions);
+  return total_len;
+}
+
+inline const Block&
+PrefixRegOptions::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+  
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+  
+inline void 
+PrefixRegOptions::wireDecode(const Block& wire)
+{
+  // PrefixRegOptions ::= PREFIX-REG-OPTIONS-TYPE TLV-LENGTH
+  //                        Name
+  //                        FaceId?
+  //                        Flags?
+  //                        Cost?    
+  //                        ExpirationPeriod?
+  //                        StrategyName?
+  //                        Protocol?
+
+  m_name.clear();
+  m_faceId = INVALID_FACE_ID;
+  m_flags = DEFAULT_FLAGS;
+  m_cost = DEFAULT_COST;
+  m_expirationPeriod = -1;
+  m_strategy.clear();
+  m_protocol.clear();
+
+  m_wire = wire;
+
+  if (m_wire.type() != tlv::nrd::PrefixRegOptions)
+    throw Error("Requested decoding of PrefixRegOptions, but Block is of different type");
+  
+  m_wire.parse ();
+
+  // Name
+  Block::element_const_iterator val = m_wire.find(Tlv::Name);
+  if (val != m_wire.elements_end())
+    {
+      m_name.wireDecode(*val);
+    }
+
+  // FaceId
+  val = m_wire.find(tlv::nrd::FaceId);
+  if (val != m_wire.elements_end())
+    {
+      m_faceId = readNonNegativeInteger(*val);
+    }
+  
+  // Flags
+  val = m_wire.find(tlv::nrd::Flags);
+  if (val != m_wire.elements_end())
+    {
+      m_flags = readNonNegativeInteger(*val);
+    }
+
+  // Cost
+  val = m_wire.find(tlv::nrd::Cost);
+  if (val != m_wire.elements_end())
+    {
+      m_cost = readNonNegativeInteger(*val);
+    }
+
+  // ExpirationPeriod
+  val = m_wire.find(tlv::nrd::ExpirationPeriod);
+  if (val != m_wire.elements_end())
+    {
+      m_expirationPeriod = readNonNegativeInteger(*val);
+    }
+
+  // StrategyName
+  val = m_wire.find(tlv::nrd::StrategyName);
+  if (val != m_wire.elements_end())
+    {
+      val->parse();
+      if (!val->elements().empty())
+        m_strategy.wireDecode(*val->elements_begin());
+    }
+
+  // Protocol
+  val = m_wire.find(tlv::nrd::Protocol);
+  if (val != m_wire.elements_end())
+    {
+      m_protocol = std::string(reinterpret_cast<const char*>(val->value()),
+                               val->value_size());
+    }
+}
+
+// inline std::ostream&
+// operator << (std::ostream &os, const PrefixRegOptions &option)
+// {
+//   os << "ForwardingEntry(";
+  
+//   // Name
+//   os << "Prefix: " << option.getName() << ", ";
+
+//   // FaceID
+//   os << "FaceID: " << option.getFaceId() << ", ";
+
+//   // Cost
+//   os << "Cost: " << option.getCost() << ", ";
+
+//   // Strategy
+//   os << "Strategy: " << option.getStrategy() << ", ";
+  
+//   os << ")";
+//   return os;
+// }
+
+} // namespace nrd
+} // namespace ndn
+
+#endif // NDN_MANAGEMENT_NRD_PREFIX_REG_OPTIONS_HPP
diff --git a/tests/management/test-nfd-control.cpp b/tests/management/test-nfd-control.cpp
index c41844a..79b4d39 100644
--- a/tests/management/test-nfd-control.cpp
+++ b/tests/management/test-nfd-control.cpp
@@ -26,7 +26,7 @@
 const uint8_t TestFibManagementOptions[] = {
   0x68, 0x1e, 0x07, 0x16, 0x08, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68,
   0x6f, 0x73, 0x74, 0x08, 0x03, 0x72, 0x65, 0x67, 0x08, 0x04, 0x74, 0x65,
-  0x73, 0x74, 0x69, 0x01, 0x00, 0x6a, 0x01, 0x00
+  0x73, 0x74, 0x69, 0x01, 0x00, 0x6a, 0x01, 0x01
 };
 
 const uint8_t TestFaceManagementOptions[] = {
@@ -65,7 +65,7 @@
 
   opt.setName (n);
   opt.setFaceId (0);
-  opt.setCost (0);
+  opt.setCost (1);
 
   const Block& blk = opt.wireEncode ();
 
@@ -81,7 +81,7 @@
 
   opt.setName (n);
   opt.setFaceId (0);
-  opt.setCost (0);
+  opt.setCost (1);
 
   EncodingBuffer blk;
 
@@ -110,7 +110,7 @@
   
   BOOST_CHECK_EQUAL (opt.getName (), n);
   BOOST_CHECK_EQUAL (opt.getFaceId (), 0);
-  BOOST_CHECK_EQUAL (opt.getCost (), 0);
+  BOOST_CHECK_EQUAL (opt.getCost (), 1);
 }
 
 BOOST_AUTO_TEST_CASE (FaceManagementOptionsFastEncoding)
diff --git a/tests/management/test-nrd.cpp b/tests/management/test-nrd.cpp
new file mode 100644
index 0000000..e45eb73
--- /dev/null
+++ b/tests/management/test-nrd.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "management/nrd-prefix-reg-options.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/output_test_stream.hpp>
+
+using namespace std;
+
+namespace ndn {
+namespace nrd {
+
+BOOST_AUTO_TEST_SUITE(TestNrd)
+
+const uint8_t RealPrefixRegOptions[] = {
+  0x65, 0x1b, 0x07, 0x16, 0x08, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
+  0x08, 0x03, 0x72, 0x65, 0x67, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x66, 0x01, 0x00,
+};
+
+BOOST_AUTO_TEST_CASE (PrefixRegOptionsEncode)
+{
+  Name n ("/localhost/reg/test");
+  PrefixRegOptions opt;
+
+  opt.setName (n);
+  opt.setFaceId (0);
+  opt.setCost (0);
+
+  const Block& blk = opt.wireEncode ();
+
+  BOOST_CHECK_EQUAL_COLLECTIONS (RealPrefixRegOptions,
+                                   RealPrefixRegOptions + sizeof (RealPrefixRegOptions),
+                                   blk.begin (), blk.end ());
+}
+
+BOOST_AUTO_TEST_CASE (PrefixRegOptionsDecoding)
+{
+  Block blk (RealPrefixRegOptions, sizeof (RealPrefixRegOptions));
+  Name n ("/localhost/reg/test");
+  PrefixRegOptions opt;
+
+  BOOST_REQUIRE_NO_THROW (opt.wireDecode (blk));
+  
+  BOOST_CHECK_EQUAL (opt.getName (), n);
+  BOOST_CHECK_EQUAL (opt.getFaceId (), 0);
+  BOOST_CHECK_EQUAL (opt.getCost (), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace nrd
+} // namespace ndn