encoding: Extending Block and EncodingBuffer interfaces

Now it is possible to use constructors to create Block from
EncodingBuffer and EncodingBuffer from Block.

Block->EncodingBuffer conversion is potentially dangerous and should be
used only in exceptional cases, such as Data packet encoding: to encode
the signed part first and then extend signed part with signature and
other related fields.

Change-Id: I5a13bf0c196ecd0d45dfa14c4cb6f4a9f612420c
diff --git a/src/common.hpp b/src/common.hpp
index 647e306..8d26a14 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -54,6 +54,10 @@
 using std::make_shared;
 using std::enable_shared_from_this;
 
+using std::static_pointer_cast;
+using std::dynamic_pointer_cast;
+using std::const_pointer_cast;
+
 using std::function;
 using std::bind;
 using std::placeholders; // _1, _2, ..
@@ -79,6 +83,10 @@
 using boost::make_shared;
 using boost::enable_shared_from_this;
 
+using boost::static_pointer_cast;
+using boost::dynamic_pointer_cast;
+using boost::const_pointer_cast;
+
 using boost::function;
 using boost::bind;
 
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
index 5409879..37b2e6f 100644
--- a/src/encoding/block.cpp
+++ b/src/encoding/block.cpp
@@ -11,6 +11,7 @@
 
 #include "block.hpp"
 #include "tlv.hpp"
+#include "encoding-buffer.hpp"
 
 namespace ndn {
 
@@ -19,6 +20,23 @@
 {
 }
 
+Block::Block(const EncodingBuffer& buffer)
+  : m_buffer(buffer.m_buffer)
+  , m_begin(buffer.begin())
+  , m_end(buffer.end())
+  , m_size(m_end - m_begin)
+{
+  m_value_begin = m_begin;
+  m_value_end   = m_end;
+
+  m_type = Tlv::readType(m_value_begin, m_value_end);
+  uint64_t length = Tlv::readVarNumber(m_value_begin, m_value_end);
+  if (length != static_cast<uint64_t>(m_value_end - m_value_begin))
+    {
+      throw Tlv::Error("TLV length doesn't match buffer length");
+    }
+}
+
 Block::Block(const ConstBufferPtr &wire,
              uint32_t type,
              const Buffer::const_iterator &begin, const Buffer::const_iterator &end,
diff --git a/src/encoding/block.hpp b/src/encoding/block.hpp
index a2f19be..895df49 100644
--- a/src/encoding/block.hpp
+++ b/src/encoding/block.hpp
@@ -39,6 +39,12 @@
   Block();
 
   /**
+   * @brief Create block based on EncodingBuffer object
+   */
+  explicit
+  Block(const EncodingBuffer& buffer);
+  
+  /**
    * @brief A helper version of a constructor to create Block from the raw buffer (type and value-length parsing)
    */
   Block(const ConstBufferPtr &buffer);
diff --git a/src/encoding/encoding-buffer.hpp b/src/encoding/encoding-buffer.hpp
index b4c9018..fe5dc3a 100644
--- a/src/encoding/encoding-buffer.hpp
+++ b/src/encoding/encoding-buffer.hpp
@@ -55,6 +55,24 @@
     m_begin = m_end = m_buffer->end () - (reserveFromBack < totalReserve ? reserveFromBack : 0);
   }
 
+  /**
+   * @brief Create EncodingBlock from existing block
+   *
+   * This is a dangerous constructor and should be used with caution.
+   * It will modify contents of the buffer that is used by block and may
+   * impact data in other blocks.
+   *
+   * The primary purpose for this method is to be used to extend Block
+   * after sign operation.
+   */
+  explicit
+  EncodingImpl (const Block& block)
+    : m_buffer(const_pointer_cast<Buffer>(block.m_buffer))
+    , m_begin(m_buffer->begin() + (block.begin() - m_buffer->begin()))
+    , m_end(m_buffer->begin()   + (block.end()   - m_buffer->begin()))
+  {
+  }
+  
   inline size_t
   size () const;
 
@@ -128,6 +146,8 @@
   Buffer::iterator m_begin;
   // invariant: m_end always points to the position of next unwritten byte (if appending data)
   Buffer::iterator m_end;
+
+  friend class Block;
 };
 
 
diff --git a/tests/test-block.cpp b/tests/test-block.cpp
index c2fab26..7ed80fa 100644
--- a/tests/test-block.cpp
+++ b/tests/test-block.cpp
@@ -12,11 +12,11 @@
 
 BOOST_AUTO_TEST_SUITE(TestBlock)
 
-BOOST_AUTO_TEST_CASE (Decode)
+BOOST_AUTO_TEST_CASE (EncodingBufferToBlock)
 {
   uint8_t value[4];
 
-  ndn::EncodingBuffer buffer;
+  EncodingBuffer buffer;
   size_t length = buffer.prependByteArray(value, sizeof(value));
   buffer.prependVarNumber(length);
   buffer.prependVarNumber(0xe0);
@@ -25,6 +25,33 @@
   BOOST_REQUIRE_NO_THROW(block = buffer.block());
   BOOST_CHECK_EQUAL(block.type(), 0xe0);
   BOOST_CHECK_EQUAL(block.value_size(), sizeof(value));
+
+  BOOST_REQUIRE_NO_THROW(block = Block(buffer));
+  BOOST_CHECK_EQUAL(block.type(), 0xe0);
+  BOOST_CHECK_EQUAL(block.value_size(), sizeof(value));
+}
+
+BOOST_AUTO_TEST_CASE (BlockToBuffer)
+{
+  shared_ptr<Buffer> buf = make_shared<Buffer>(10);
+  for (int i = 0; i < 10; i++) (*buf)[i] = i;
+  
+  Block block(0xab, buf);
+  block.encode();
+
+  EncodingBuffer buffer(0,0);
+  BOOST_REQUIRE_NO_THROW(buffer = EncodingBuffer(block));
+  BOOST_CHECK_EQUAL(buffer.size(), 12);
+  BOOST_CHECK_EQUAL(buffer.capacity(), 12);
+
+  (*buf)[1] = 0xe0;
+  (*buf)[2] = 2;
+  BOOST_REQUIRE_NO_THROW(block = Block(buf, buf->begin() + 1, buf->begin() + 5));
+  BOOST_CHECK_EQUAL(block.type(), 0xe0);
+
+  BOOST_REQUIRE_NO_THROW(buffer = EncodingBuffer(block));
+  BOOST_CHECK_EQUAL(buffer.size(), 4);
+  BOOST_CHECK_EQUAL(buffer.capacity(), 10);
 }
 
 BOOST_AUTO_TEST_SUITE_END()