management: add CachingPolicy to LocalControlHeader

Breaks: NFD:commit:9cfeecaa2ffad1a1be79aed2321dcc7d042ecc5f
Breaks: ndnSIM:commit:ffe2586cfc466935c0632a5a64384f0fda709086

Change-Id: Idcce2b7ee9be31b55f9ec16c17e915d93974bff4
Refs: #2183
diff --git a/src/data.cpp b/src/data.cpp
index fc3dfa7..f127a12 100644
--- a/src/data.cpp
+++ b/src/data.cpp
@@ -300,6 +300,15 @@
   return *this;
 }
 
+Data&
+Data::setCachingPolicy(nfd::LocalControlHeader::CachingPolicy cachingPolicy)
+{
+  getLocalControlHeader().setCachingPolicy(cachingPolicy);
+  // ! do not reset Data's wire !
+
+  return *this;
+}
+
 void
 Data::onChanged()
 {
diff --git a/src/data.hpp b/src/data.hpp
index 2f56dfc..078938a 100644
--- a/src/data.hpp
+++ b/src/data.hpp
@@ -297,6 +297,12 @@
   Data&
   setIncomingFaceId(uint64_t incomingFaceId);
 
+  nfd::LocalControlHeader::CachingPolicy
+  getCachingPolicy() const;
+
+  Data&
+  setCachingPolicy(nfd::LocalControlHeader::CachingPolicy cachingPolicy);
+
 public: // EqualityComparable concept
   bool
   operator==(const Data& other) const;
@@ -387,6 +393,11 @@
   return getLocalControlHeader().getIncomingFaceId();
 }
 
+inline nfd::LocalControlHeader::CachingPolicy
+Data::getCachingPolicy() const
+{
+  return getLocalControlHeader().getCachingPolicy();
+}
 
 } // namespace ndn
 
diff --git a/src/detail/face-impl.hpp b/src/detail/face-impl.hpp
index 6844813..f6d7d91 100644
--- a/src/detail/face-impl.hpp
+++ b/src/detail/face-impl.hpp
@@ -119,11 +119,11 @@
 
     m_pendingInterestTable.push_back(make_shared<PendingInterest>(interest, onData, onTimeout));
 
-    if (!interest->getLocalControlHeader().empty(false, true))
+    if (!interest->getLocalControlHeader().empty(nfd::LocalControlHeader::ENCODE_NEXT_HOP))
       {
         // encode only NextHopFaceId towards the forwarder
         m_face.m_transport->send(interest->getLocalControlHeader()
-                                   .wireEncode(*interest, false, true),
+                                   .wireEncode(*interest, nfd::LocalControlHeader::ENCODE_NEXT_HOP),
                                  interest->wireEncode());
       }
     else
@@ -149,10 +149,12 @@
   {
     this->ensureConnected();
 
-    if (!data->getLocalControlHeader().empty(false, false))
+    if (!data->getLocalControlHeader().empty(nfd::LocalControlHeader::ENCODE_CACHING_POLICY))
       {
-        m_face.m_transport->send(data->getLocalControlHeader().wireEncode(*data, false, false),
-                                 data->wireEncode());
+        m_face.m_transport->send(
+          data->getLocalControlHeader().wireEncode(*data,
+                                                   nfd::LocalControlHeader::ENCODE_CACHING_POLICY),
+          data->wireEncode());
       }
     else
       {
diff --git a/src/encoding/tlv-nfd.hpp b/src/encoding/tlv-nfd.hpp
index c6c330b..77533df 100644
--- a/src/encoding/tlv-nfd.hpp
+++ b/src/encoding/tlv-nfd.hpp
@@ -94,7 +94,9 @@
   // Local Control Header
   LocalControlHeader = 80,
   IncomingFaceId     = 81,
-  NextHopFaceId      = 82
+  NextHopFaceId      = 82,
+  CachingPolicy      = 83,
+  NoCache            = 96
 };
 
 } // namespace nfd
diff --git a/src/management/nfd-local-control-header.hpp b/src/management/nfd-local-control-header.hpp
index 5f8d667..8108230 100644
--- a/src/management/nfd-local-control-header.hpp
+++ b/src/management/nfd-local-control-header.hpp
@@ -47,9 +47,23 @@
     }
   };
 
+  enum EncodeFlags : uint8_t {
+    ENCODE_NONE             = 0,
+    ENCODE_INCOMING_FACE_ID = (1 << 0),
+    ENCODE_NEXT_HOP         = (1 << 1),
+    ENCODE_CACHING_POLICY   = (1 << 2),
+    ENCODE_ALL              = 0xff
+  };
+
+  enum CachingPolicy : uint8_t {
+    INVALID_POLICY = 0,
+    NO_CACHE       = 1
+  };
+
   LocalControlHeader()
     : m_incomingFaceId(INVALID_FACE_ID)
     , m_nextHopFaceId(INVALID_FACE_ID)
+    , m_cachingPolicy(CachingPolicy::INVALID_POLICY)
   {
   }
 
@@ -59,10 +73,9 @@
    * @sa wireDecode
    */
   explicit
-  LocalControlHeader(const Block& wire,
-                     bool encodeIncomingFaceId = true, bool encodeNextHopFaceId = true)
+  LocalControlHeader(const Block& wire, uint8_t encodeMask = ENCODE_ALL)
   {
-    wireDecode(wire, encodeIncomingFaceId, encodeNextHopFaceId);
+    wireDecode(wire, encodeMask);
   }
 
   /**
@@ -71,8 +84,8 @@
    * The caller is responsible of checking whether LocalControlHeader contains
    * any information.
    *
-   * !It is an error to call this method if neither IncomingFaceId nor NextHopFaceId is
-   * set, or neither of them is enabled.
+   * !It is an error to call this method if none of IncomingFaceId, NextHopFaceId and CachingPolicy
+   * are set, or neither of them are enabled.
    *
    * @throws LocalControlHeader::Error when empty LocalControlHeader be produced
    *
@@ -86,8 +99,7 @@
    */
   template<class U>
   inline Block
-  wireEncode(const U& payload,
-             bool encodeIncomingFaceId, bool encodeNextHopFaceId) const;
+  wireEncode(const U& payload, uint8_t encodeMask = ENCODE_ALL) const;
 
   /**
    * @brief Decode from the wire format and set LocalControlHeader on the supplied item
@@ -96,8 +108,7 @@
    * LocalControlHeader should be done before calling this method.
    */
   inline void
-  wireDecode(const Block& wire,
-             bool encodeIncomingFaceId = true, bool encodeNextHopFaceId = true);
+  wireDecode(const Block& wire, uint8_t encodeMask = ENCODE_ALL);
 
   inline static const Block&
   getPayload(const Block& wire);
@@ -108,10 +119,15 @@
   // Getters/setters
 
   bool
-  empty(bool encodeIncomingFaceId, bool encodeNextHopFaceId) const
+  empty(uint8_t encodeMask) const
   {
-    return !((encodeIncomingFaceId && hasIncomingFaceId()) ||
-             (encodeNextHopFaceId  && hasNextHopFaceId()));
+    bool needIncomingFaceId = encodeMask & ENCODE_INCOMING_FACE_ID;
+    bool needNextHopFaceId = encodeMask & ENCODE_NEXT_HOP;
+    bool needCachingPolicy = encodeMask & ENCODE_CACHING_POLICY;
+
+    return !((needIncomingFaceId && hasIncomingFaceId()) ||
+             (needNextHopFaceId  && hasNextHopFaceId())  ||
+             (needCachingPolicy  && hasCachingPolicy()));
   }
 
   //
@@ -154,15 +170,35 @@
     m_nextHopFaceId = nextHopFaceId;
   }
 
+  //
+
+  bool
+  hasCachingPolicy() const
+  {
+    return m_cachingPolicy != CachingPolicy::INVALID_POLICY;
+  }
+
+  CachingPolicy
+  getCachingPolicy() const
+  {
+    return m_cachingPolicy;
+  }
+
+  void
+  setCachingPolicy(CachingPolicy cachingPolicy)
+  {
+    m_cachingPolicy = cachingPolicy;
+  }
+
 private:
   template<encoding::Tag TAG>
   inline size_t
-  wireEncode(EncodingImpl<TAG>& block, size_t payloadSize,
-             bool encodeIncomingFaceId, bool encodeNextHopFaceId) const;
+  wireEncode(EncodingImpl<TAG>& block, size_t payloadSize, uint8_t encodeMask) const;
 
 private:
   uint64_t m_incomingFaceId;
   uint64_t m_nextHopFaceId;
+  CachingPolicy m_cachingPolicy;
 };
 
 
@@ -172,22 +208,37 @@
 template<encoding::Tag TAG>
 inline size_t
 LocalControlHeader::wireEncode(EncodingImpl<TAG>& block, size_t payloadSize,
-                               bool encodeIncomingFaceId, bool encodeNextHopFaceId) const
+                               uint8_t encodeMask) const
 {
+  bool needIncomingFaceId = encodeMask & ENCODE_INCOMING_FACE_ID;
+  bool needNextHopFaceId = encodeMask & ENCODE_NEXT_HOP;
+  bool needCachingPolicy = encodeMask & ENCODE_CACHING_POLICY;
+
   size_t totalLength = payloadSize;
 
-  if (encodeIncomingFaceId && hasIncomingFaceId())
+  if (needIncomingFaceId && hasIncomingFaceId())
     {
       totalLength += prependNonNegativeIntegerBlock(block,
                                                     tlv::nfd::IncomingFaceId, getIncomingFaceId());
     }
 
-  if (encodeNextHopFaceId && hasNextHopFaceId())
+  if (needNextHopFaceId && hasNextHopFaceId())
     {
       totalLength += prependNonNegativeIntegerBlock(block,
                                                     tlv::nfd::NextHopFaceId, getNextHopFaceId());
     }
 
+  if (needCachingPolicy && hasCachingPolicy())
+    {
+      size_t cachingPolicyLength = 0;
+      cachingPolicyLength += block.prependVarNumber(0);
+      cachingPolicyLength += block.prependVarNumber(tlv::nfd::NoCache);
+      cachingPolicyLength += block.prependVarNumber(cachingPolicyLength);
+      cachingPolicyLength += block.prependVarNumber(tlv::nfd::CachingPolicy);
+
+      totalLength += cachingPolicyLength;
+    }
+
   totalLength += block.prependVarNumber(totalLength);
   totalLength += block.prependVarNumber(tlv::nfd::LocalControlHeader);
   return totalLength;
@@ -195,34 +246,34 @@
 
 template<class U>
 inline Block
-LocalControlHeader::wireEncode(const U& payload,
-                               bool encodeIncomingFaceId, bool encodeNextHopFaceId) const
+LocalControlHeader::wireEncode(const U& payload, uint8_t encodeMask) const
 {
   /// @todo should this be BOOST_ASSERT instead?  This is kind of unnecessary overhead
-  if (empty(encodeIncomingFaceId, encodeNextHopFaceId))
+  if (empty(encodeMask))
     throw Error("Requested wire for LocalControlHeader, but none of the fields are set or enabled");
 
   EncodingEstimator estimator;
-  size_t length = wireEncode(estimator, payload.wireEncode().size(),
-                             encodeIncomingFaceId, encodeNextHopFaceId);
+  size_t length = wireEncode(estimator, payload.wireEncode().size(), encodeMask);
 
   EncodingBuffer buffer(length);
-  wireEncode(buffer, payload.wireEncode().size(),
-             encodeIncomingFaceId, encodeNextHopFaceId);
+  wireEncode(buffer, payload.wireEncode().size(), encodeMask);
 
   return buffer.block(false);
 }
 
 inline void
-LocalControlHeader::wireDecode(const Block& wire,
-                               bool encodeIncomingFaceId/* = true*/,
-                               bool encodeNextHopFaceId/* = true*/)
+LocalControlHeader::wireDecode(const Block& wire, uint8_t encodeMask)
 {
+  bool needIncomingFaceId = encodeMask & ENCODE_INCOMING_FACE_ID;
+  bool needNextHopFaceId = encodeMask & ENCODE_NEXT_HOP;
+  bool needCachingPolicy = encodeMask & ENCODE_CACHING_POLICY;
+
   BOOST_ASSERT(wire.type() == tlv::nfd::LocalControlHeader);
   wire.parse();
 
   m_incomingFaceId = INVALID_FACE_ID;
   m_nextHopFaceId = INVALID_FACE_ID;
+  m_cachingPolicy = CachingPolicy::INVALID_POLICY;
 
   for (Block::element_const_iterator i = wire.elements_begin();
        i != wire.elements_end();
@@ -231,13 +282,25 @@
       switch (i->type())
         {
         case tlv::nfd::IncomingFaceId:
-          if (encodeIncomingFaceId)
+          if (needIncomingFaceId)
             m_incomingFaceId = readNonNegativeInteger(*i);
           break;
         case tlv::nfd::NextHopFaceId:
-          if (encodeNextHopFaceId)
+          if (needNextHopFaceId)
             m_nextHopFaceId = readNonNegativeInteger(*i);
           break;
+        case tlv::nfd::CachingPolicy:
+          if (needCachingPolicy) {
+            i->parse();
+            Block::element_const_iterator it = i->elements_begin();
+            if (it != i->elements_end() && it->type() == tlv::nfd::NoCache) {
+              m_cachingPolicy = CachingPolicy::NO_CACHE;
+            }
+            else {
+              throw Error("CachingPolicy: Missing required NoCache field");
+            }
+          }
+          break;
         default:
           // ignore all unsupported
           break;
@@ -262,7 +325,6 @@
     }
 }
 
-
 } // namespace nfd
 } // namespace ndn
 
diff --git a/src/util/dummy-client-face.cpp b/src/util/dummy-client-face.cpp
index f5e1172..24348e9 100644
--- a/src/util/dummy-client-face.cpp
+++ b/src/util/dummy-client-face.cpp
@@ -169,9 +169,10 @@
 DummyClientFace::receive(const Packet& packet)
 {
   // do not restrict what injected control header can contain
-  if (!packet.getLocalControlHeader().empty(true, true)) {
+  if (!packet.getLocalControlHeader().empty(nfd::LocalControlHeader::ENCODE_ALL)) {
 
-    Block header = packet.getLocalControlHeader().wireEncode(packet, true, true);
+    Block header = packet.getLocalControlHeader().wireEncode(packet,
+                                                             nfd::LocalControlHeader::ENCODE_ALL);
     Block payload = packet.wireEncode();
 
     EncodingBuffer encoder(header.size() + payload.size(), header.size() + payload.size());