face: NDNLP slicer

refs #1206

Change-Id: Ifbf0aeedff8fc9300799c97772d988dce2fce306
diff --git a/daemon/face/face.hpp b/daemon/face/face.hpp
index 58cccf6..68b8073 100644
--- a/daemon/face/face.hpp
+++ b/daemon/face/face.hpp
@@ -21,8 +21,7 @@
 
 const std::size_t MAX_NDN_PACKET_SIZE = 8800;
 
-/** \class Face
- *  \brief represents a face
+/** \brief represents a face
  */
 class Face : noncopyable, public enable_shared_from_this<Face>
 {
diff --git a/daemon/face/ndnlp-sequence-generator.cpp b/daemon/face/ndnlp-sequence-generator.cpp
new file mode 100644
index 0000000..01dfd14
--- /dev/null
+++ b/daemon/face/ndnlp-sequence-generator.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "ndnlp-sequence-generator.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+SequenceBlock::SequenceBlock(uint64_t start, size_t count)
+  : m_start(start)
+  , m_count(count)
+{
+}
+
+SequenceGenerator::SequenceGenerator()
+  : m_next(0)
+{
+}
+
+SequenceBlock
+SequenceGenerator::nextBlock(size_t count)
+{
+  SequenceBlock sb(m_next, count);
+  m_next += count;
+  return sb;
+}
+
+} // namespace ndnlp
+} // namespace nfd
diff --git a/daemon/face/ndnlp-sequence-generator.hpp b/daemon/face/ndnlp-sequence-generator.hpp
new file mode 100644
index 0000000..f067552
--- /dev/null
+++ b/daemon/face/ndnlp-sequence-generator.hpp
@@ -0,0 +1,72 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_NDNLP_SEQUENCE_GENERATOR_HPP
+#define NFD_FACE_NDNLP_SEQUENCE_GENERATOR_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+/** \brief represents a block of sequence numbers
+ */
+class SequenceBlock
+{
+public:
+  SequenceBlock(uint64_t start, size_t count);
+  
+  /** \return{ the pos-th sequence number }
+   */
+  uint64_t
+  operator[](size_t pos) const;
+  
+  size_t
+  count() const;
+  
+private:
+  uint64_t m_start;
+  size_t m_count;
+};
+
+inline uint64_t
+SequenceBlock::operator[](size_t pos) const
+{
+  if (pos >= m_count)
+    throw std::out_of_range("pos");
+  return m_start + static_cast<uint64_t>(pos);
+}
+
+inline size_t
+SequenceBlock::count() const
+{
+  return m_count;
+}
+
+/** \class SequenceGenerator
+ *  \brief generates sequence numbers
+ */
+class SequenceGenerator : noncopyable
+{
+public:
+  SequenceGenerator();
+  
+  /** \brief generates a block of consecutive sequence numbers
+   *
+   *  This block must not overlap with a recent block.
+   */
+  SequenceBlock
+  nextBlock(size_t count);
+
+private:
+  /// next sequence number
+  uint64_t m_next;
+};
+
+} // namespace ndnlp
+} // namespace nfd
+
+#endif // NFD_FACE_NDNLP_SEQUENCE_GENERATOR_HPP
diff --git a/daemon/face/ndnlp-slicer.cpp b/daemon/face/ndnlp-slicer.cpp
new file mode 100644
index 0000000..074a4f7
--- /dev/null
+++ b/daemon/face/ndnlp-slicer.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "ndnlp-slicer.hpp"
+
+#include <ndn-cpp-dev/encoding/encoding-buffer.hpp>
+
+namespace nfd {
+namespace ndnlp {
+
+Slicer::Slicer(size_t mtu)
+  : m_mtu(mtu)
+{
+  this->estimateOverhead();
+}
+
+Slicer::~Slicer()
+{
+}
+
+template<bool T>
+static inline size_t
+Slicer_encodeFragment(ndn::EncodingImpl<T>& blk,
+                      uint64_t seq, uint16_t fragIndex, uint16_t fragCount,
+                      const uint8_t* payload, size_t payloadSize)
+{
+  size_t totalLength = 0;
+  
+  // NdnlpPayload
+  size_t payloadLength = blk.prependByteArray(payload, payloadSize);
+  totalLength += payloadLength;
+  totalLength += blk.prependVarNumber(payloadLength);
+  totalLength += blk.prependVarNumber(tlv::NdnlpPayload);
+  
+  bool needFragIndexAndCount = fragCount > 1;
+  if (needFragIndexAndCount) {
+    // NdnlpFragCount
+    uint16_t fragCountBE = htobe16(fragCount);
+    size_t fragCountLength = blk.prependByteArray(
+      reinterpret_cast<uint8_t*>(&fragCountBE), sizeof(fragCountBE));
+    totalLength += fragCountLength;
+    totalLength += blk.prependVarNumber(fragCountLength);
+    totalLength += blk.prependVarNumber(tlv::NdnlpFragCount);
+    
+    // NdnlpFragIndex
+    uint16_t fragIndexBE = htobe16(fragIndex);
+    size_t fragIndexLength = blk.prependByteArray(
+      reinterpret_cast<uint8_t*>(&fragIndexBE), sizeof(fragIndexBE));
+    totalLength += fragIndexLength;
+    totalLength += blk.prependVarNumber(fragIndexLength);
+    totalLength += blk.prependVarNumber(tlv::NdnlpFragIndex);
+  }
+  
+  // NdnlpSequence
+  uint64_t sequenceBE = htobe64(seq);
+  size_t sequenceLength = blk.prependByteArray(
+    reinterpret_cast<uint8_t*>(&sequenceBE), sizeof(sequenceBE));
+  totalLength += sequenceLength;
+  totalLength += blk.prependVarNumber(sequenceLength);
+  totalLength += blk.prependVarNumber(tlv::NdnlpSequence);
+  
+  totalLength += blk.prependVarNumber(totalLength);
+  totalLength += blk.prependVarNumber(tlv::NdnlpData);
+  
+  return totalLength;
+}
+
+void
+Slicer::estimateOverhead()
+{
+  ndn::EncodingEstimator estimator;
+  size_t estimatedSize = Slicer_encodeFragment(estimator,
+                         0, 0, 2, 0, m_mtu);
+  
+  size_t overhead = estimatedSize - m_mtu;
+  m_maxPayload = m_mtu - overhead;
+}
+
+PacketArray
+Slicer::slice(const Block& block)
+{
+  BOOST_ASSERT(block.hasWire());
+  const uint8_t* networkPacket = block.wire();
+  size_t networkPacketSize = block.size();
+  
+  uint16_t fragCount = static_cast<uint16_t>(
+                         (networkPacketSize / m_maxPayload) +
+                         (networkPacketSize % m_maxPayload == 0 ? 0 : 1)
+                       );
+  PacketArray pa = make_shared<std::vector<Block> >();
+  pa->reserve(fragCount);
+  SequenceBlock seqBlock = m_seqgen.nextBlock(fragCount);
+  
+  for (uint16_t fragIndex = 0; fragIndex < fragCount; ++fragIndex) {
+    size_t payloadOffset = fragIndex * m_maxPayload;
+    const uint8_t* payload = networkPacket + payloadOffset;
+    size_t payloadSize = std::min(m_maxPayload, networkPacketSize - payloadOffset);
+    
+    ndn::EncodingBuffer buffer(m_mtu, 0);
+    size_t pktSize = Slicer_encodeFragment(buffer,
+      seqBlock[fragIndex], fragIndex, fragCount, payload, payloadSize);
+    
+    BOOST_ASSERT(pktSize <= m_mtu);
+    
+    pa->push_back(buffer.block());
+  }
+  
+  return pa;
+}
+
+} // namespace ndnlp
+} // namespace nfd
diff --git a/daemon/face/ndnlp-slicer.hpp b/daemon/face/ndnlp-slicer.hpp
new file mode 100644
index 0000000..ba04e3c
--- /dev/null
+++ b/daemon/face/ndnlp-slicer.hpp
@@ -0,0 +1,50 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_NDNLP_SLICER_HPP
+#define NFD_FACE_NDNLP_SLICER_HPP
+
+#include "ndnlp-tlv.hpp"
+#include "ndnlp-sequence-generator.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+typedef shared_ptr<std::vector<Block> > PacketArray;
+
+/** \brief provides fragmentation feature at sender
+ */
+class Slicer : noncopyable
+{
+public:
+  explicit
+  Slicer(size_t mtu);
+  
+  virtual
+  ~Slicer();
+  
+  PacketArray
+  slice(const Block& block);
+
+private:
+  /// estimate the size of NDNLP header and maximum payload size per packet
+  void
+  estimateOverhead();
+  
+private:
+  SequenceGenerator m_seqgen;
+  
+  /// maximum packet size
+  size_t m_mtu;
+  
+  /// maximum payload size
+  size_t m_maxPayload;
+};
+
+} // namespace ndnlp
+} // namespace nfd
+
+#endif // NFD_FACE_NDNLP_SLICER_HPP
diff --git a/daemon/face/ndnlp-tlv.hpp b/daemon/face/ndnlp-tlv.hpp
new file mode 100644
index 0000000..fe15e6a
--- /dev/null
+++ b/daemon/face/ndnlp-tlv.hpp
@@ -0,0 +1,24 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_NDNLP_TLV_HPP
+#define NFD_FACE_NDNLP_TLV_HPP
+
+namespace nfd {
+namespace tlv {
+
+enum {
+  NdnlpData      = 80,
+  NdnlpSequence  = 81,
+  NdnlpFragIndex = 82,
+  NdnlpFragCount = 83,
+  NdnlpPayload   = 84
+};
+
+} // namespace tlv
+} // namespace nfd
+
+#endif // NFD_FACE_NDNLP_TLV_HPP