encoding: avoid misaligned memory accesses in TLV decoding

ndn::tlv::readVarNumber and ndn::tlv::readNonNegativeInteger
now properly support InputIterator, and have efficient
implementations for ContiguousIterator that do not rely on
unsafe misaligned memory accesses.

refs #4172, #4097

Change-Id: Ib02b8d908ad5cfec474679b5b3b5ccd95ba07549
diff --git a/src/encoding/tlv.hpp b/src/encoding/tlv.hpp
index 876fcb5..4e9c009 100644
--- a/src/encoding/tlv.hpp
+++ b/src/encoding/tlv.hpp
@@ -25,6 +25,7 @@
 #include "buffer.hpp"
 #include "endian.hpp"
 
+#include <cstring>
 #include <iostream>
 #include <iterator>
 
@@ -138,7 +139,7 @@
 
 /**
  * @brief Read VAR-NUMBER in NDN-TLV encoding
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
  *
  * @param [inout] begin  Begin of the buffer, will be incremented to point to the first byte after
  *                       the read VAR-NUMBER
@@ -148,13 +149,13 @@
  * @return true if number was successfully read from input, false otherwise
  * @note This call never throws exceptions
  */
-template<class InputIterator>
+template<typename Iterator>
 bool
-readVarNumber(InputIterator& begin, const InputIterator& end, uint64_t& number);
+readVarNumber(Iterator& begin, const Iterator& end, uint64_t& number);
 
 /**
  * @brief Read TLV-TYPE
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
  *
  * @param [inout] begin  Begin of the buffer, will be incremented to point to the first byte after
  *                       the read TLV-TYPE
@@ -166,13 +167,13 @@
  * @note This call is largely equivalent to tlv::readVarNumber, but it will return false if type
  *       is larger than 2^32-1 (TLV-TYPE in this library is implemented as uint32_t)
  */
-template<class InputIterator>
+template<typename Iterator>
 bool
-readType(InputIterator& begin, const InputIterator& end, uint32_t& type);
+readType(Iterator& begin, const Iterator& end, uint32_t& type);
 
 /**
  * @brief Read VAR-NUMBER in NDN-TLV encoding
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
  *
  * @param [inout] begin Begin of the buffer, will be incremented to point to the first byte after
  *                      the read VAR-NUMBER
@@ -180,13 +181,13 @@
  *
  * @throw tlv::Error VAR-NUMBER cannot be read
  */
-template<class InputIterator>
+template<typename Iterator>
 uint64_t
-readVarNumber(InputIterator& begin, const InputIterator& end);
+readVarNumber(Iterator& begin, const Iterator& end);
 
 /**
  * @brief Read TLV Type
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
  *
  * @param [inout] begin Begin of the buffer, will be incremented to point to the first byte after
  *                      the read TLV-TYPE
@@ -196,26 +197,26 @@
  * @note This call is largely equivalent to tlv::readVarNumber, but exception will be thrown if type
  *       is larger than 2^32-1 (TLV-TYPE in this library is implemented as uint32_t)
  */
-template<class InputIterator>
+template<typename Iterator>
 uint32_t
-readType(InputIterator& begin, const InputIterator& end);
+readType(Iterator& begin, const Iterator& end);
 
 /**
  * @brief Get number of bytes necessary to hold value of VAR-NUMBER
  */
 constexpr size_t
-sizeOfVarNumber(uint64_t varNumber);
+sizeOfVarNumber(uint64_t number);
 
 /**
  * @brief Write VAR-NUMBER to the specified stream
  * @return length of written VAR-NUMBER
  */
 size_t
-writeVarNumber(std::ostream& os, uint64_t varNumber);
+writeVarNumber(std::ostream& os, uint64_t number);
 
 /**
  * @brief Read nonNegativeInteger in NDN-TLV encoding
- * @tparam InputIterator an iterator or pointer dereferencable to uint8_t
+ * @tparam Iterator an iterator or pointer whose value is assignable to uint8_t
  *
  * @param [in]    size  size of the nonNegativeInteger
  * @param [inout] begin Begin of the buffer, will be incremented to point to the first byte after
@@ -226,9 +227,9 @@
  * @note How many bytes to read is directly controlled by \p size, which can be either 1, 2, 4, or 8.
  *       If \p size differs from \p std::distance(begin, end), tlv::Error exception will be thrown.
  */
-template<class InputIterator>
+template<typename Iterator>
 uint64_t
-readNonNegativeInteger(size_t size, InputIterator& begin, const InputIterator& end);
+readNonNegativeInteger(size_t size, Iterator& begin, const Iterator& end);
 
 /**
  * @brief Get number of bytes necessary to hold value of nonNegativeInteger
@@ -253,9 +254,106 @@
 /////////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////////
 
-template<class InputIterator>
+namespace detail {
+
+/** @brief Function object to read a number from InputIterator
+ */
+template<typename Iterator>
+class ReadNumberSlow
+{
+public:
+  bool
+  operator()(size_t size, Iterator& begin, const Iterator& end, uint64_t& number) const
+  {
+    number = 0;
+    size_t count = 0;
+    for (; begin != end && count < size; ++begin, ++count) {
+      number = (number << 8) | *begin;
+    }
+    return count == size;
+  }
+};
+
+/** @brief Function object to read a number from ContiguousIterator
+ */
+template<typename Iterator>
+class ReadNumberFast
+{
+public:
+  bool
+  operator()(size_t size, Iterator& begin, const Iterator& end, uint64_t& number) const
+  {
+    if (begin + size > end) {
+      return false;
+    }
+
+    switch (size) {
+      case 1: {
+        number = *begin;
+        ++begin;
+        return true;
+      }
+      case 2: {
+        uint16_t value = 0;
+        std::memcpy(&value, &*begin, 2);
+        begin += 2;
+        number = be16toh(value);
+        return true;
+      }
+      case 4: {
+        uint32_t value = 0;
+        std::memcpy(&value, &*begin, 4);
+        begin += 4;
+        number = be32toh(value);
+        return true;
+      }
+      case 8: {
+        uint64_t value = 0;
+        std::memcpy(&value, &*begin, 8);
+        begin += 8;
+        number = be64toh(value);
+        return true;
+      }
+      default: {
+        BOOST_ASSERT(false);
+        return false;
+      }
+    }
+  }
+};
+
+/** @brief Determine whether to select ReadNumber implementation for ContiguousIterator
+ *
+ *  This is not a full ContiguousIterator detection implementation. It returns true for the most
+ *  common ContiguousIterator types used with TLV decoding function templates.
+ */
+template<typename Iterator,
+         typename DecayedIterator = typename std::decay<Iterator>::type,
+         typename ValueType = typename std::iterator_traits<DecayedIterator>::value_type>
+constexpr bool
+shouldSelectContiguousReadNumber()
+{
+  return (std::is_convertible<DecayedIterator, const ValueType*>::value ||
+          std::is_convertible<DecayedIterator, typename std::basic_string<ValueType>::const_iterator>::value ||
+          std::is_convertible<DecayedIterator, typename std::vector<ValueType>::const_iterator>::value) &&
+         (std::is_same<ValueType, uint8_t>::value ||
+          std::is_same<ValueType, int8_t>::value ||
+          std::is_same<ValueType, char>::value ||
+          std::is_same<ValueType, unsigned char>::value ||
+          std::is_same<ValueType, signed char>::value);
+}
+
+template<typename Iterator>
+class ReadNumber : public std::conditional<shouldSelectContiguousReadNumber<Iterator>(),
+                                           ReadNumberFast<Iterator>, ReadNumberSlow<Iterator>>::type
+{
+};
+
+} // namespace detail
+
+template<typename Iterator>
 bool
-readVarNumber(InputIterator& begin, const InputIterator& end, uint64_t& number)
+readVarNumber(Iterator& begin, const Iterator& end, uint64_t& number)
 {
   if (begin == end)
     return false;
@@ -264,39 +362,17 @@
   ++begin;
   if (firstOctet < 253) {
     number = firstOctet;
-  }
-  else if (firstOctet == 253) {
-    if (end - begin < 2)
-      return false;
-
-    uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin);
-    begin += 2;
-    number = be16toh(value);
-  }
-  else if (firstOctet == 254) {
-    if (end - begin < 4)
-      return false;
-
-    uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin);
-    begin += 4;
-    number = be32toh(value);
-  }
-  else { // if (firstOctet == 255)
-    if (end - begin < 8)
-      return false;
-
-    uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
-    begin += 8;
-
-    number = be64toh(value);
+    return true;
   }
 
-  return true;
+  size_t size = firstOctet == 253 ? 2 :
+                firstOctet == 254 ? 4 : 8;
+  return detail::ReadNumber<Iterator>()(size, begin, end, number);
 }
 
-template<class InputIterator>
+template<typename Iterator>
 bool
-readType(InputIterator& begin, const InputIterator& end, uint32_t& type)
+readType(Iterator& begin, const Iterator& end, uint32_t& type)
 {
   uint64_t number = 0;
   bool isOk = readVarNumber(begin, end, number);
@@ -308,51 +384,25 @@
   return true;
 }
 
-template<class InputIterator>
+template<typename Iterator>
 uint64_t
-readVarNumber(InputIterator& begin, const InputIterator& end)
+readVarNumber(Iterator& begin, const Iterator& end)
 {
   if (begin == end)
     BOOST_THROW_EXCEPTION(Error("Empty buffer during TLV processing"));
 
-  uint64_t value;
+  uint64_t value = 0;
   bool isOk = readVarNumber(begin, end, value);
-  if (!isOk)
+  if (!isOk) {
     BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
+  }
 
   return value;
 }
 
-template<>
-inline bool
-readVarNumber<std::istream_iterator<uint8_t>>(std::istream_iterator<uint8_t>& begin,
-                                              const std::istream_iterator<uint8_t>& end,
-                                              uint64_t& value)
-{
-  if (begin == end)
-    return false;
-
-  uint8_t firstOctet = *begin;
-  ++begin;
-  if (firstOctet < 253) {
-    value = firstOctet;
-    return true;
-  }
-
-  size_t expectedSize = firstOctet == 253 ? 2 :
-                        firstOctet == 254 ? 4 : 8;
-  value = 0;
-  size_t count = 0;
-  for (; begin != end && count < expectedSize; ++count) {
-    value = (value << 8) | *begin;
-    ++begin;
-  }
-  return count == expectedSize;
-}
-
-template<class InputIterator>
+template<typename Iterator>
 uint32_t
-readType(InputIterator& begin, const InputIterator& end)
+readType(Iterator& begin, const Iterator& end)
 {
   uint64_t type = readVarNumber(begin, end);
   if (type > std::numeric_limits<uint32_t>::max()) {
@@ -363,105 +413,56 @@
 }
 
 constexpr size_t
-sizeOfVarNumber(uint64_t varNumber)
+sizeOfVarNumber(uint64_t number)
 {
-  return varNumber < 253 ? 1 :
-         varNumber <= std::numeric_limits<uint16_t>::max() ? 3 :
-         varNumber <= std::numeric_limits<uint32_t>::max() ? 5 : 9;
+  return number < 253 ? 1 :
+         number <= std::numeric_limits<uint16_t>::max() ? 3 :
+         number <= std::numeric_limits<uint32_t>::max() ? 5 : 9;
 }
 
 inline size_t
-writeVarNumber(std::ostream& os, uint64_t varNumber)
+writeVarNumber(std::ostream& os, uint64_t number)
 {
-  if (varNumber < 253) {
-    os.put(static_cast<char>(varNumber));
+  if (number < 253) {
+    os.put(static_cast<char>(number));
     return 1;
   }
-  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+  else if (number <= std::numeric_limits<uint16_t>::max()) {
     os.put(static_cast<char>(253));
-    uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
+    uint16_t value = htobe16(static_cast<uint16_t>(number));
     os.write(reinterpret_cast<const char*>(&value), 2);
     return 3;
   }
-  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+  else if (number <= std::numeric_limits<uint32_t>::max()) {
     os.put(static_cast<char>(254));
-    uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
+    uint32_t value = htobe32(static_cast<uint32_t>(number));
     os.write(reinterpret_cast<const char*>(&value), 4);
     return 5;
   }
   else {
     os.put(static_cast<char>(255));
-    uint64_t value = htobe64(varNumber);
+    uint64_t value = htobe64(number);
     os.write(reinterpret_cast<const char*>(&value), 8);
     return 9;
   }
 }
 
-template<class InputIterator>
+template<typename Iterator>
 uint64_t
-readNonNegativeInteger(size_t size, InputIterator& begin, const InputIterator& end)
-{
-  switch (size) {
-    case 1: {
-      if (end - begin < 1)
-        BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
-
-      uint8_t value = *begin;
-      begin++;
-      return value;
-    }
-    case 2: {
-      if (end - begin < 2)
-        BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
-
-      uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin);
-      begin += 2;
-      return be16toh(value);
-    }
-    case 4: {
-      if (end - begin < 4)
-        BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
-
-      uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin);
-      begin += 4;
-      return be32toh(value);
-    }
-    case 8: {
-      if (end - begin < 8)
-        BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
-
-      uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
-      begin += 8;
-      return be64toh(value);
-    }
-  }
-  BOOST_THROW_EXCEPTION(Error("Invalid length for nonNegativeInteger "
-                              "(only 1, 2, 4, and 8 are allowed)"));
-}
-
-template<>
-inline uint64_t
-readNonNegativeInteger<std::istream_iterator<uint8_t>>(size_t size,
-                                                       std::istream_iterator<uint8_t>& begin,
-                                                       const std::istream_iterator<uint8_t>& end)
+readNonNegativeInteger(size_t size, Iterator& begin, const Iterator& end)
 {
   if (size != 1 && size != 2 && size != 4 && size != 8) {
     BOOST_THROW_EXCEPTION(Error("Invalid length for nonNegativeInteger "
                                 "(only 1, 2, 4, and 8 are allowed)"));
   }
 
-  uint64_t value = 0;
-  size_t count = 0;
-  for (; begin != end && count < size; ++count) {
-    value = (value << 8) | *begin;
-    begin++;
-  }
-
-  if (count != size) {
+  uint64_t number = 0;
+  bool isOk = detail::ReadNumber<Iterator>()(size, begin, end, number);
+  if (!isOk) {
     BOOST_THROW_EXCEPTION(Error("Insufficient data during TLV processing"));
   }
 
-  return value;
+  return number;
 }
 
 constexpr size_t