decoding+transport: Exception-safe Block parsing

Change-Id: I3e83b6ca4c8ca42b8bb1ddc8dc50c52ee366c55c
Refs: #1291
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
index eaba9ba..07ec2f0 100644
--- a/src/encoding/block.cpp
+++ b/src/encoding/block.cpp
@@ -59,7 +59,7 @@
 {
   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);
@@ -93,9 +93,9 @@
 
 Block::Block(std::istream& is)
 {
-  std::istream_iterator<uint8_t> tmp_begin(is);  
+  std::istream_iterator<uint8_t> tmp_begin(is);
   std::istream_iterator<uint8_t> tmp_end;
-  
+
   m_type = Tlv::readType(tmp_begin, tmp_end);
   uint64_t length = Tlv::readVarNumber(tmp_begin, tmp_end);
 
@@ -104,7 +104,7 @@
   OBufferStream os;
   size_t headerLength = Tlv::writeVarNumber(os, m_type);
   headerLength += Tlv::writeVarNumber(os, length);
-  
+
   char* buf = new char[length];
   buf[0] = *tmp_begin;
   is.read(buf+1, length-1);
@@ -113,7 +113,7 @@
     {
       delete [] buf;
       throw Tlv::Error("Not enough data in the buffer to fully parse TLV");
-    } 
+    }
 
   os.write(buf, length);
   delete [] buf;
@@ -132,11 +132,11 @@
 Block::Block(const uint8_t *buffer, size_t maxlength)
 {
   const uint8_t * tmp_begin = buffer;
-  const uint8_t * tmp_end   = buffer + maxlength;  
-  
+  const uint8_t * tmp_end   = buffer + maxlength;
+
   m_type = Tlv::readType(tmp_begin, tmp_end);
   uint64_t length = Tlv::readVarNumber(tmp_begin, tmp_end);
-  
+
   if (length > static_cast<uint64_t>(tmp_end - tmp_begin))
     {
       throw Tlv::Error("Not enough data in the buffer to fully parse TLV");
@@ -155,13 +155,13 @@
 Block::Block(const void *bufferX, size_t maxlength)
 {
   const uint8_t * buffer = reinterpret_cast<const uint8_t*>(bufferX);
-  
+
   const uint8_t * tmp_begin = buffer;
-  const uint8_t * tmp_end   = buffer + maxlength;  
-  
+  const uint8_t * tmp_end   = buffer + maxlength;
+
   m_type = Tlv::readType(tmp_begin, tmp_end);
   uint64_t length = Tlv::readVarNumber(tmp_begin, tmp_end);
-  
+
   if (length > static_cast<uint64_t>(tmp_end - tmp_begin))
     {
       throw Tlv::Error("Not enough data in the buffer to fully parse TLV");
@@ -204,19 +204,75 @@
   m_size = Tlv::sizeOfVarNumber(m_type) + Tlv::sizeOfVarNumber(value_size()) + value_size();
 }
 
+bool
+Block::fromBuffer(const ConstBufferPtr& wire, size_t offset, Block& block)
+{
+  Buffer::const_iterator tempBegin = wire->begin() + offset;
+
+  uint32_t type;
+  bool ok = Tlv::readType(tempBegin, wire->end(), type);
+  if (!ok)
+    return false;
+
+  uint64_t length;
+  ok = Tlv::readVarNumber(tempBegin, wire->end(), length);
+  if (!ok)
+    return false;
+
+  if (length > static_cast<uint64_t>(wire->end() - tempBegin))
+    {
+      return false;
+    }
+
+  block = Block(wire, type,
+                wire->begin() + offset, tempBegin + length,
+                tempBegin, tempBegin + length);
+
+  return true;
+}
+
+bool
+Block::fromBuffer(const uint8_t* buffer, size_t maxSize, Block& block)
+{
+  const uint8_t* tempBegin = buffer;
+  const uint8_t* tempEnd = buffer + maxSize;
+
+  uint32_t type;
+  bool ok = Tlv::readType(tempBegin, tempEnd, type);
+  if (!ok)
+    return false;
+
+  uint64_t length;
+  ok = Tlv::readVarNumber(tempBegin, tempEnd, length);
+  if (!ok)
+    return false;
+
+  if (length > static_cast<uint64_t>(tempEnd - tempBegin))
+    {
+      return false;
+    }
+
+  BufferPtr sharedBuffer = make_shared<Buffer>(buffer, tempBegin + length);
+  block = Block(sharedBuffer, type,
+                sharedBuffer->begin(), sharedBuffer->end(),
+                sharedBuffer->begin() + (tempBegin - buffer), sharedBuffer->end());
+
+  return true;
+}
+
 void
 Block::parse() const
 {
   if (!m_subBlocks.empty() || value_size()==0)
     return;
-  
+
   Buffer::const_iterator begin = value_begin(),
     end = value_end();
 
   while (begin != end)
     {
       Buffer::const_iterator element_begin = begin;
-      
+
       uint32_t type = Tlv::readType(begin, end);
       uint64_t length = Tlv::readVarNumber(begin, end);
 
@@ -226,7 +282,7 @@
           throw Tlv::Error("TLV length exceeds buffer length");
         }
       Buffer::const_iterator element_end = begin + length;
-      
+
       m_subBlocks.push_back(Block(m_buffer,
                                   type,
                                   element_begin, element_end,
@@ -261,7 +317,7 @@
       for (element_const_iterator i = m_subBlocks.begin(); i != m_subBlocks.end(); ++i) {
         valueSize += i->size();
       }
-  
+
       Tlv::writeVarNumber(os, valueSize);
 
       for (element_const_iterator i = m_subBlocks.begin(); i != m_subBlocks.end(); ++i) {
@@ -286,7 +342,7 @@
 
   m_value_begin = m_buffer->begin();
   m_value_end   = m_buffer->end();
-  
+
   Tlv::readType(m_value_begin, m_value_end);
   Tlv::readVarNumber(m_value_begin, m_value_end);
 }
@@ -296,18 +352,18 @@
 {
   if (value_size()==0)
     throw Error("Underlying value buffer is empty");
-  
+
   Buffer::const_iterator begin = value_begin(),
     end = value_end();
 
   Buffer::const_iterator element_begin = begin;
-      
+
   uint32_t type = Tlv::readType(begin, end);
   uint64_t length = Tlv::readVarNumber(begin, end);
 
   if (length != static_cast<uint64_t>(end - begin))
     throw Tlv::Error("TLV length mismatches buffer length");
-      
+
   return Block(m_buffer,
                type,
                element_begin, end,
diff --git a/src/encoding/block.hpp b/src/encoding/block.hpp
index 52ed7e7..a2507b5 100644
--- a/src/encoding/block.hpp
+++ b/src/encoding/block.hpp
@@ -31,8 +31,11 @@
   typedef element_container::const_iterator  element_const_iterator;
 
   /// @brief Error that can be thrown from the block
-  struct Error : public std::runtime_error { Error(const std::string &what) : std::runtime_error(what) {} };
-  
+  struct Error : public std::runtime_error
+  {
+    Error(const std::string& what) : std::runtime_error(what) {}
+  };
+
   /**
    * @brief Default constructor to create an empty Block
    */
@@ -43,11 +46,11 @@
    */
   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);
+  Block(const ConstBufferPtr& buffer);
 
   /**
    * @brief Another helper to create block from a buffer, directly specifying boundaries
@@ -55,31 +58,31 @@
    *
    * This version will automatically detect type and position of the value within the block
    */
-  Block(const ConstBufferPtr &buffer,
-        const Buffer::const_iterator &begin, const Buffer::const_iterator &end,
+  Block(const ConstBufferPtr& buffer,
+        const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
         bool verifyLength = true);
-  
+
   /**
    * @brief A helper version of a constructor to create Block from the raw buffer (type and value-length parsing)
    */
-  Block(const uint8_t *buffer, size_t maxlength);
+  Block(const uint8_t* buffer, size_t maxlength);
 
-  Block(const void *buffer, size_t maxlength);
+  Block(const void* buffer, size_t maxlength);
 
   /*
-   * @brief A helper version of a constructor to create Block from the stream. 
+   * @brief A helper version of a constructor to create Block from the stream.
    */
   Block(std::istream& is);
-  
+
   /**
    * @brief Create Block from the wire buffer (no parsing)
    *
    * This version of the constructor does not do any parsing
    */
-  Block(const ConstBufferPtr &wire,
+  Block(const ConstBufferPtr& wire,
         uint32_t type,
-        const Buffer::const_iterator &begin, const Buffer::const_iterator &end,
-        const Buffer::const_iterator &valueBegin, const Buffer::const_iterator &valueEnd);
+        const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
+        const Buffer::const_iterator& valueBegin, const Buffer::const_iterator& valueEnd);
 
   /**
    * @brief Create Block of a specific type with empty wire buffer
@@ -94,7 +97,7 @@
    * to construct wire encoding, one need to prepend the wire buffer with type
    * and value-length VAR-NUMBERs
    */
-  Block(uint32_t type, const ConstBufferPtr &value);
+  Block(uint32_t type, const ConstBufferPtr& value);
 
   /**
    * @brief Create nested Block of a specific type with the specified value
@@ -104,14 +107,34 @@
    * and value-length VAR-NUMBERs
    */
   explicit
-  Block(uint32_t type, const Block &value);
+  Block(uint32_t type, const Block& value);
+
+  /**
+   * @brief Try to construct block from Buffer, referencing data block pointed by wire
+   *
+   * @throws This method never throws an exception
+   *
+   * @returns true if Block successfully created, false if block cannot be created
+   */
+  static bool
+  fromBuffer(const ConstBufferPtr& wire, size_t offset, Block& block);
+
+  /**
+   * @brief Try to construct block from Buffer, referencing data block pointed by wire
+   *
+   * @throws This method never throws an exception
+   *
+   * @returns true if Block successfully created, false if block cannot be created
+   */
+  static bool
+  fromBuffer(const uint8_t* buffer, size_t maxSize, Block& block);
 
   /**
    * @brief Check if the Block is empty
    */
   inline bool
   empty() const;
-  
+
   /**
    * @brief Check if the Block has fully encoded wire
    */
@@ -123,7 +146,7 @@
    */
   inline bool
   hasValue() const;
-  
+
   /**
    * @brief Reset wire buffer of the element
    */
@@ -150,14 +173,14 @@
    */
   void
   encode();
-  
+
   inline uint32_t
   type() const;
 
   /**
    * @brief Get the first subelement of the requested type
    */
-  inline const Block &
+  inline const Block&
   get(uint32_t type) const;
 
   inline element_const_iterator
@@ -171,10 +194,10 @@
 
   inline element_iterator
   erase(element_iterator first, element_iterator last);
-  
+
   inline void
-  push_back(const Block &element);
-  
+  push_back(const Block& element);
+
   inline Buffer::const_iterator
   begin() const;
 
@@ -189,7 +212,7 @@
 
   // inline const uint8_t*
   // buf() const;
-  
+
   inline Buffer::const_iterator
   value_begin() const;
 
@@ -216,7 +239,7 @@
 
   inline size_t
   elements_size() const;
-  
+
   Block
   blockFromValue() const;
 
@@ -224,11 +247,11 @@
   ConstBufferPtr m_buffer;
 
   uint32_t m_type;
-  
+
   Buffer::const_iterator m_begin;
   Buffer::const_iterator m_end;
   uint32_t m_size;
-  
+
   Buffer::const_iterator m_value_begin;
   Buffer::const_iterator m_value_end;
 
@@ -285,7 +308,7 @@
   return m_type;
 }
 
-inline const Block &
+inline const Block&
 Block::get(uint32_t type) const
 {
   for (element_const_iterator i = m_subBlocks.begin ();
@@ -300,7 +323,7 @@
 
   throw Error("(Block::get) Requested a non-existed type [" + boost::lexical_cast<std::string>(type) + "] from Block");
 }
-  
+
 inline Block::element_const_iterator
 Block::find(uint32_t type) const
 {
@@ -409,7 +432,7 @@
 {
   if (!hasValue())
     return 0;
-  
+
   return &*m_value_begin;
 }
 
diff --git a/src/encoding/tlv.hpp b/src/encoding/tlv.hpp
index 8a2b3fc..2ef008c 100644
--- a/src/encoding/tlv.hpp
+++ b/src/encoding/tlv.hpp
@@ -69,7 +69,39 @@
 /**
  * @brief Read VAR-NUMBER in NDN-TLV encoding
  *
- * This call will throw ndn::Tlv::Error (aka std::runtime_error) if number cannot be read
+ * @param [in]  begin  Begin (pointer or iterator) of the buffer
+ * @param [in]  end    End (pointer or iterator) of the buffer
+ * @param [out] number Read number
+ *
+ * @throws This call never throws exception
+ *
+ * @return true if number successfully read from input, false otherwise
+ */
+template<class InputIterator>
+inline bool
+readVarNumber(InputIterator &begin, const InputIterator &end, uint64_t& number);
+
+/**
+ * @brief Read TLV Type
+ *
+ * @param [in]  begin  Begin (pointer or iterator) of the buffer
+ * @param [in]  end    End (pointer or iterator) of the buffer
+ * @param [out] number Read type number
+ *
+ * @throws This call never throws exception
+ *
+ * This call is largely equivalent to tlv::readVarNumber, but exception will be thrown if type
+ * is larger than 2^32-1 (type in this library is implemented as uint32_t)
+ */
+template<class InputIterator>
+inline bool
+readType(InputIterator &begin, const InputIterator &end, uint32_t& type);
+
+
+/**
+ * @brief Read VAR-NUMBER in NDN-TLV encoding
+ *
+ * @throws This call will throw ndn::Tlv::Error (aka std::runtime_error) if number cannot be read
  *
  * Note that after call finished, begin will point to the first byte after the read VAR-NUMBER
  */
@@ -80,6 +112,8 @@
 /**
  * @brief Read TLV Type
  *
+ * @throws This call will throw ndn::Tlv::Error (aka std::runtime_error) if number cannot be read
+ *
  * This call is largely equivalent to tlv::readVarNumber, but exception will be thrown if type
  * is larger than 2^32-1 (type in this library is implemented as uint32_t)
  */
@@ -136,12 +170,72 @@
 /////////////////////////////////////////////////////////////////////////////////
 
 template<class InputIterator>
+inline bool
+readVarNumber(InputIterator &begin, const InputIterator &end, uint64_t& number)
+{
+  if (begin == end)
+    return false;
+
+  uint8_t value = *begin;
+  ++begin;
+  if (value < 253)
+    {
+      number = value;
+    }
+  else if (value == 253)
+    {
+      if (end - begin < 2)
+        return false;
+
+      uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin); // kind of dangerous... but should be efficient
+      begin += 2;
+      number = be16toh(value);
+    }
+  else if (value == 254)
+    {
+      if (end - begin < 4)
+        return false;
+
+      uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin); // kind of dangerous... but should be efficient
+      begin += 4;
+      number = be32toh(value);
+    }
+  else // if (value == 255)
+    {
+      if (end - begin < 8)
+        return false;
+
+      uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
+      begin += 8;
+
+      number = be64toh(value);
+    }
+
+  return true;
+}
+
+template<class InputIterator>
+inline bool
+readType(InputIterator &begin, const InputIterator &end, uint32_t& type)
+{
+  uint64_t number;
+  bool ok = readVarNumber(begin, end, number);
+  if (number > std::numeric_limits<uint32_t>::max())
+    {
+      return false;
+    }
+
+  type = static_cast<uint64_t> (number);
+  return true;
+}
+
+template<class InputIterator>
 inline uint64_t
 readVarNumber(InputIterator &begin, const InputIterator &end)
 {
   if (begin == end)
     throw Error("Empty buffer during TLV processing");
-  
+
   uint8_t value = *begin;
   ++begin;
   if (value < 253)
@@ -152,7 +246,7 @@
     {
       if (end - begin < 2)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin); // kind of dangerous... but should be efficient
       begin += 2;
       return be16toh(value);
@@ -161,7 +255,7 @@
     {
       if (end - begin < 4)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin); // kind of dangerous... but should be efficient
       begin += 4;
       return be32toh(value);
@@ -170,7 +264,7 @@
     {
       if (end - begin < 8)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
       begin += 8;
 
@@ -180,12 +274,12 @@
 
 template<>
 inline uint64_t
-readVarNumber<std::istream_iterator<uint8_t> >(std::istream_iterator<uint8_t> &begin, 
+readVarNumber<std::istream_iterator<uint8_t> >(std::istream_iterator<uint8_t> &begin,
                                                const std::istream_iterator<uint8_t> &end)
 {
   if (begin == end)
     throw Error("Empty buffer during TLV processing");
-  
+
   uint8_t value = *begin;
   ++begin;
   if (value < 253)
@@ -196,7 +290,7 @@
     {
       uint8_t buffer[2];
       int count = 0;
-      
+
       while(begin != end && count < 2){
         buffer[count] = *begin;
         begin++;
@@ -205,15 +299,15 @@
 
       if (count < 2)
         throw Error("Insufficient data during TLV processing");
-      
-      uint16_t value = *reinterpret_cast<const uint16_t*>(buffer); 
+
+      uint16_t value = *reinterpret_cast<const uint16_t*>(buffer);
       return be16toh(value);
     }
   else if (value == 254)
     {
       uint8_t buffer[4];
       int count = 0;
-      
+
       while(begin != end && count < 4){
         buffer[count] = *begin;
         begin++;
@@ -222,7 +316,7 @@
 
       if (count < 4)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint32_t value = *reinterpret_cast<const uint32_t*>(buffer);
       return be32toh(value);
     }
@@ -230,7 +324,7 @@
     {
       uint8_t buffer[8];
       int count = 0;
-      
+
       while(begin != end && count < 8){
         buffer[count] = *begin;
         begin++;
@@ -239,7 +333,7 @@
 
       if (count < 8)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint64_t value = *reinterpret_cast<const uint64_t*>(buffer);
       return be64toh(value);
     }
@@ -311,7 +405,7 @@
     {
       if (end - begin < 1)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint8_t value = *begin;
       begin++;
       return value;
@@ -320,7 +414,7 @@
     {
       if (end - begin < 2)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin); // kind of dangerous... but should be efficient
       begin += 2;
       return be16toh(value);
@@ -329,7 +423,7 @@
     {
       if (end - begin < 4)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin); // kind of dangerous... but should be efficient
       begin += 4;
       return be32toh(value);
@@ -338,7 +432,7 @@
     {
       if (end - begin < 8)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
       begin += 8;
       return be64toh(value);
@@ -349,8 +443,8 @@
 
 template<>
 inline uint64_t
-readNonNegativeInteger<std::istream_iterator<uint8_t> >(size_t size, 
-                                                        std::istream_iterator<uint8_t> &begin, 
+readNonNegativeInteger<std::istream_iterator<uint8_t> >(size_t size,
+                                                        std::istream_iterator<uint8_t> &begin,
                                                         const std::istream_iterator<uint8_t> &end)
 {
   switch (size) {
@@ -358,7 +452,7 @@
     {
       if(begin == end)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint8_t value = *begin;
       begin++;
       return value;
@@ -367,16 +461,16 @@
     {
       uint8_t buffer[2];
       int count = 0;
-      
+
       while(begin != end && count < 2){
         buffer[count] = *begin;
         begin++;
         count++;
       }
-      
+
       if (count < 2)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint16_t value = *reinterpret_cast<const uint16_t*>(buffer);
       return be16toh(value);
     }
@@ -384,7 +478,7 @@
     {
       uint8_t buffer[4];
       int count = 0;
-      
+
       while(begin != end && count < 4){
         buffer[count] = *begin;
         begin++;
@@ -393,7 +487,7 @@
 
       if (count < 4)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint32_t value = *reinterpret_cast<const uint32_t*>(buffer);
       return be32toh(value);
     }
@@ -401,7 +495,7 @@
     {
       uint8_t buffer[8];
       int count = 0;
-      
+
       while(begin != end && count < 8){
         buffer[count] = *begin;
         begin++;
@@ -410,7 +504,7 @@
 
       if (count < 8)
         throw Error("Insufficient data during TLV processing");
-      
+
       uint64_t value = *reinterpret_cast<const uint64_t*>(buffer);
       return be64toh(value);
     }