encoding: Add TLV-related definitions, data structures, abstraction, and functions

Change-Id: I2dbdd5ae17f1f60cdd65b7e46e5b43b25c7f025c
diff --git a/include/ndn-cpp/encoding/tlv.hpp b/include/ndn-cpp/encoding/tlv.hpp
new file mode 100644
index 0000000..0edf94a
--- /dev/null
+++ b/include/ndn-cpp/encoding/tlv.hpp
@@ -0,0 +1,326 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_TLV_HPP
+#define NDN_TLV_HPP
+
+#include "buffer.hpp"
+#include "endian.h"
+
+namespace ndn {
+
+/**
+ * @brief Namespace defining NDN-TLV related constants and procedures
+ */
+namespace Tlv {
+
+struct Error : public std::runtime_error { Error(const std::string &what) : std::runtime_error(what) {} };
+
+enum {
+  Interest      = 0,
+  Data          = 1,
+  Name          = 2,
+  NameComponent = 3,
+  Selectors     = 4,
+  Nonce         = 5,
+  Scope         = 6,
+  InterestLifetime          = 7,
+  MinSuffixComponents       = 8,
+  MaxSuffixComponents       = 9,
+  PublisherPublicKeyLocator = 10,
+  Exclude       = 11,
+  ChildSelector = 12,
+  MustBeFresh   = 13,
+  Any           = 14,
+  MetaInfo      = 15,
+  Content       = 16,
+  SignatureInfo = 17,
+  SignatureValue = 18,
+  ContentType   = 19,
+  FreshnessPeriod = 20,
+  SignatureType = 21,
+  KeyLocator    = 22,
+  KeyLocatorDigest = 23,
+
+  AppPrivateBlock1 = 128,
+  AppPrivateBlock2 = 32767
+};
+
+enum SignatureType {
+  DigestSha256 = 0,
+  SignatureSha256WithRsa = 1,
+};
+
+enum ConentType {
+  ContentType_Default = 0,
+  ContentType_Link = 1,
+  ContentType_Key = 2,
+};
+
+/**
+ * @brief Read VAR-NUMBER in NDN-TLV encoding
+ *
+ * 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
+ */
+template<class InputIterator>
+inline uint64_t
+readVarNumber(InputIterator &begin, const InputIterator &end);
+
+/**
+ * @brief Read TLV Type
+ *
+ * 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 uint32_t
+readType(InputIterator &begin, const InputIterator &end);
+
+/**
+ * @brief Get number of bytes necessary to hold value of VAR-NUMBER
+ */
+inline size_t
+sizeOfVarNumber(uint64_t varNumber);
+
+/**
+ * @brief Write VAR-NUMBER to the specified stream
+ */
+inline size_t
+writeVarNumber(std::ostream &os, uint64_t varNumber);
+
+/**
+ * @brief Read nonNegativeInteger in NDN-TLV encoding
+ *
+ * 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
+ *
+ * How many bytes will be read is directly controlled by the size parameter, which can be either
+ * 1, 2, 4, or 8.  If the value of size is different, then an exception will be thrown.
+ */
+template<class InputIterator>
+inline uint64_t
+readNonNegativeInteger(size_t size, InputIterator &begin, const InputIterator &end);
+
+/**
+ * @brief Get number of bytes necessary to hold value of nonNegativeInteger
+ */
+inline size_t
+sizeOfNonNegativeInteger(uint64_t varNumber);
+
+/**
+ * @brief Write nonNegativeInteger to the specified stream
+ */
+inline size_t
+writeNonNegativeInteger(std::ostream &os, uint64_t varNumber);
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+// Inline implementations
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+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)
+    {
+      return value;
+    }
+  else if (value == 253)
+    {
+      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);
+    }
+  else if (value == 254)
+    {
+      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);
+    }
+  else // if (value == 255)
+    {
+      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);
+    }
+}
+
+template<class InputIterator>
+inline uint32_t
+readType(InputIterator &begin, const InputIterator &end)
+{
+  uint64_t type   = readVarNumber(begin, end);
+  if (type > std::numeric_limits<uint32_t>::max())
+    {
+      throw Error("TLV type code exceeds allowed maximum");
+    }
+
+  return static_cast<uint32_t> (type);
+}
+
+size_t
+sizeOfVarNumber(uint64_t varNumber)
+{
+  if (varNumber < 253) {
+    return 1;
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    return 3;
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    return 5;
+  }
+  else {
+    return 9;
+  }
+}
+
+inline size_t
+writeVarNumber(std::ostream &os, uint64_t varNumber)
+{
+  if (varNumber < 253) {
+    os.put(static_cast<uint8_t> (varNumber));
+    return 1;
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    os.put(253);
+    uint16_t value = htobe16(static_cast<uint16_t> (varNumber));
+    os.write(reinterpret_cast<const char*> (&value), 2);
+    return 3;
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    os.put(254);
+    uint32_t value = htobe32(static_cast<uint32_t> (varNumber));
+    os.write(reinterpret_cast<const char*> (&value), 4);
+    return 5;
+  }
+  else {
+    os.put(255);
+    uint64_t value = htobe64(varNumber);
+    os.write(reinterpret_cast<const char*> (&value), 8);
+    return 9;
+  }
+}
+
+template<class InputIterator>
+inline uint64_t
+readNonNegativeInteger(size_t size, InputIterator &begin, const InputIterator &end)
+{
+  switch (size) {
+  case 1:
+    {
+      if (end - begin < 1)
+        throw Error("Insufficient data during TLV processing");
+      
+      uint8_t value = *begin;
+      begin++;
+      return value;
+    }
+  case 2:
+    {
+      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);
+    }
+  case 4:
+    {
+      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);
+    }
+  case 8:
+    {
+      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);
+    }
+  }
+  throw Error("Invalid length for nonNegativeInteger (only 1, 2, 4, and 8 are allowed)");
+}
+
+inline size_t
+sizeOfNonNegativeInteger(uint64_t varNumber)
+{
+  if (varNumber < 253) {
+    return 1;
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    return 2;
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    return 4;
+  }
+  else {
+    return 8;
+  }
+}
+
+
+inline size_t
+writeNonNegativeInteger(std::ostream &os, uint64_t varNumber)
+{
+  if (varNumber < 253) {
+    os.put(static_cast<uint8_t> (varNumber));
+    return 1;
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    uint16_t value = htobe16(static_cast<uint16_t> (varNumber));
+    os.write(reinterpret_cast<const char*> (&value), 2);
+    return 2;
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    uint32_t value = htobe32(static_cast<uint32_t> (varNumber));
+    os.write(reinterpret_cast<const char*> (&value), 4);
+    return 4;
+  }
+  else {
+    uint64_t value = htobe64(varNumber);
+    os.write(reinterpret_cast<const char*> (&value), 8);
+    return 8;
+  }
+}
+
+
+} // namespace tlv
+} // namespace ndn
+
+#endif // NDN_TLV_HPP