block: Add constructor to create sub-blocks using the underlying buffer

Change-Id: Ic25f82526228dbe7aa3222bc6b1e68ac0e35cea1
Refs: #3100
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
index 624439d..7306276 100644
--- a/src/encoding/block.cpp
+++ b/src/encoding/block.cpp
@@ -112,13 +112,36 @@
 
   m_type = tlv::readType(m_value_begin, m_value_end);
   uint64_t length = tlv::readVarNumber(m_value_begin, m_value_end);
-  if (verifyLength)
-    {
-      if (length != static_cast<uint64_t>(m_value_end - m_value_begin))
-        {
-          BOOST_THROW_EXCEPTION(tlv::Error("TLV length doesn't match buffer length"));
-        }
+  if (verifyLength) {
+    if (length != static_cast<uint64_t>(std::distance(m_value_begin, m_value_end))) {
+      BOOST_THROW_EXCEPTION(tlv::Error("TLV length doesn't match buffer length"));
     }
+  }
+}
+
+Block::Block(const Block& block,
+             const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
+             bool verifyLength/* = true*/)
+  : m_buffer(block.m_buffer)
+  , m_begin(begin)
+  , m_end(end)
+  , m_size(m_end - m_begin)
+{
+  if (!(m_buffer->begin() <= begin && begin <= m_buffer->end()) ||
+      !(m_buffer->begin() <= end   && end   <= m_buffer->end())) {
+    BOOST_THROW_EXCEPTION(Error("begin/end iterators do not point to the underlying buffer of the block"));
+  }
+
+  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 (verifyLength) {
+    if (length != static_cast<uint64_t>(std::distance(m_value_begin, m_value_end))) {
+      BOOST_THROW_EXCEPTION(tlv::Error("TLV length doesn't match buffer length"));
+    }
+  }
 }
 
 Block::Block(const uint8_t* buffer, size_t maxlength)
diff --git a/src/encoding/block.hpp b/src/encoding/block.hpp
index 0552755..0c83149 100644
--- a/src/encoding/block.hpp
+++ b/src/encoding/block.hpp
@@ -81,6 +81,15 @@
         const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
         bool verifyLength = true);
 
+  /** @brief Create a Block from existing block (reusing the underlying buffer), directly
+   *         specifying boundaries of the block within the buffer
+   *
+   *  This overload will automatically detect type and position of the value within the block
+   */
+  Block(const Block& block,
+        const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
+        bool verifyLength = true);
+
   /** @brief Create a Block from the raw buffer with Type-Length parsing
    */
   Block(const uint8_t* buffer, size_t maxlength);
diff --git a/tests/unit-tests/encoding/block.t.cpp b/tests/unit-tests/encoding/block.t.cpp
index cc90ede..3ae6aa1 100644
--- a/tests/unit-tests/encoding/block.t.cpp
+++ b/tests/unit-tests/encoding/block.t.cpp
@@ -167,6 +167,25 @@
 
 BOOST_AUTO_TEST_SUITE_END() // Basic
 
+BOOST_AUTO_TEST_CASE(BlockFromBlock)
+{
+  static uint8_t buffer[] = {0x80, 0x06, 0x81, 0x01, 0x01, 0x82, 0x01, 0x01};
+  Block block(buffer, sizeof(buffer));
+
+  Block derivedBlock(block, block.begin(), block.end());
+  BOOST_CHECK_EQUAL(derivedBlock.wire(), block.wire()); // pointers should match
+  BOOST_CHECK(derivedBlock == block); // blocks should match
+
+  derivedBlock = Block(block, block.begin() + 2, block.begin() + 5);
+  BOOST_CHECK(derivedBlock.begin() == block.begin() + 2);
+  BOOST_CHECK(derivedBlock == Block(buffer + 2, 3));
+
+  Buffer otherBuffer(buffer, sizeof(buffer));
+  BOOST_CHECK_THROW(Block(block, otherBuffer.begin(), block.end()), Block::Error);
+  BOOST_CHECK_THROW(Block(block, block.begin(), otherBuffer.end()), Block::Error);
+  BOOST_CHECK_THROW(Block(block, otherBuffer.begin(), otherBuffer.end()), Block::Error);
+}
+
 BOOST_AUTO_TEST_CASE(EncodingBufferToBlock)
 {
   uint8_t value[4];