name-component: recognize typed name components

refs #4526

Change-Id: I9d32a5dc216c6b0921d181573a4e043fccc2814e
diff --git a/src/encoding/tlv.hpp b/src/encoding/tlv.hpp
index 57ee509..fac222b 100644
--- a/src/encoding/tlv.hpp
+++ b/src/encoding/tlv.hpp
@@ -65,7 +65,7 @@
   Data          = 6,
   Name          = 7,
   ImplicitSha256DigestComponent = 1,
-  NameComponent = 8,
+  GenericNameComponent = 8,
   Selectors     = 9,
   Nonce         = 10,
   InterestLifetime          = 12,
@@ -90,10 +90,15 @@
   LinkPreference  = 30,
   LinkDelegation  = 31,
 
+  NameComponentMin = 1,
+  NameComponentMax = 65535,
+
   AppPrivateBlock1 = 128,
   AppPrivateBlock2 = 32767
 };
 
+constexpr int NameComponent NDN_CXX_DEPRECATED = GenericNameComponent;
+
 enum SignatureTypeValue : uint16_t {
   DigestSha256 = 0,
   SignatureSha256WithRsa = 1,
diff --git a/src/exclude.cpp b/src/exclude.cpp
index f352948..bf6bfef 100644
--- a/src/exclude.cpp
+++ b/src/exclude.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -194,6 +194,9 @@
     catch (const name::Component::Error&) {
       BOOST_THROW_EXCEPTION(Error("Incorrect format of Exclude filter"));
     }
+    if (!component.isGeneric() && !component.isImplicitSha256Digest()) {
+      BOOST_THROW_EXCEPTION(Error("Excluded component must be generic or ImplicitSha256Digest"));
+    }
     ++i;
 
     if (i != m_wire.elements_end() && i->type() == tlv::Any) {
diff --git a/src/exclude.hpp b/src/exclude.hpp
index 0bd6155..a5a2db6 100644
--- a/src/exclude.hpp
+++ b/src/exclude.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -33,6 +33,11 @@
 
 /**
  * @brief Represents Exclude selector in NDN Interest
+ *
+ * NDN Packet Format v0.3 defines name component types other than GenericNameComponent and
+ * ImplicitSha256DigestComponent, and removes Exclude selector. This implementation follows v0.2
+ * semantics and can only store GenericNameComponent and ImplicitSha256DigestComponent.
+ * The behavior of \c isExcluded on a name component of other types is unspecified.
  */
 class Exclude
 {
diff --git a/src/name-component.cpp b/src/name-component.cpp
index fb89f65..bd551b2 100644
--- a/src/name-component.cpp
+++ b/src/name-component.cpp
@@ -51,102 +51,114 @@
   return prefix;
 }
 
-Component::Component()
-  : Block(tlv::NameComponent)
+void
+Component::ensureValid() const
 {
+  if (type() < tlv::NameComponentMin || type() > tlv::NameComponentMax) {
+    BOOST_THROW_EXCEPTION(Error("TLV-TYPE " + to_string(type()) + " is not a valid NameComponent"));
+  }
+  if (type() == tlv::ImplicitSha256DigestComponent && value_size() != util::Sha256::DIGEST_SIZE) {
+    BOOST_THROW_EXCEPTION(Error("ImplicitSha256DigestComponent TLV-LENGTH must be " +
+                                to_string(util::Sha256::DIGEST_SIZE)));
+  }
+}
+
+Component::Component(uint32_t type)
+  : Block(type)
+{
+  ensureValid();
 }
 
 Component::Component(const Block& wire)
   : Block(wire)
 {
-  if (!isGeneric() && !isImplicitSha256Digest())
-    BOOST_THROW_EXCEPTION(Error("Cannot construct name::Component from not a NameComponent "
-                                "or ImplicitSha256DigestComponent TLV wire block"));
+  ensureValid();
 }
 
-Component::Component(const ConstBufferPtr& buffer)
-  : Block(tlv::NameComponent, buffer)
+Component::Component(uint32_t type, ConstBufferPtr buffer)
+  : Block(type, std::move(buffer))
 {
 }
 
-Component::Component(const Buffer& value)
-  : Block(makeBinaryBlock(tlv::NameComponent, value.data(), value.size()))
-{
-}
-
-Component::Component(const uint8_t* value, size_t valueLen)
-  : Block(makeBinaryBlock(tlv::NameComponent, value, valueLen))
+Component::Component(uint32_t type, const uint8_t* value, size_t valueLen)
+  : Block(makeBinaryBlock(type, value, valueLen))
 {
 }
 
 Component::Component(const char* str)
-  : Block(makeBinaryBlock(tlv::NameComponent, str, std::char_traits<char>::length(str)))
+  : Block(makeBinaryBlock(tlv::GenericNameComponent, str, std::char_traits<char>::length(str)))
 {
 }
 
 Component::Component(const std::string& str)
-  : Block(makeStringBlock(tlv::NameComponent, str))
+  : Block(makeStringBlock(tlv::GenericNameComponent, str))
 {
 }
 
+static Component
+parseSha256DigestUri(std::string input)
+{
+  input.erase(0, getSha256DigestUriPrefix().size());
+
+  try {
+    return Component::fromImplicitSha256Digest(fromHex(input));
+  }
+  catch (const StringHelperError&) {
+    BOOST_THROW_EXCEPTION(Component::Error("Cannot convert to a ImplicitSha256DigestComponent "
+                                           "(invalid hex encoding)"));
+  }
+}
+
 Component
-Component::fromEscapedString(const char* escapedString, size_t beginOffset, size_t endOffset)
+Component::fromEscapedString(std::string input)
 {
-  std::string trimmedString(escapedString + beginOffset, escapedString + endOffset);
-  boost::algorithm::trim(trimmedString);
-
-  if (trimmedString.compare(0, getSha256DigestUriPrefix().size(),
-                            getSha256DigestUriPrefix()) == 0) {
-    if (trimmedString.size() != getSha256DigestUriPrefix().size() + util::Sha256::DIGEST_SIZE * 2)
-      BOOST_THROW_EXCEPTION(Error("Cannot convert to ImplicitSha256DigestComponent"
-                                  "(expected sha256 in hex encoding)"));
-
-    try {
-      trimmedString.erase(0, getSha256DigestUriPrefix().size());
-      return fromImplicitSha256Digest(fromHex(trimmedString));
+  boost::algorithm::trim(input);
+  uint32_t type = tlv::GenericNameComponent;
+  size_t equalPos = input.find('=');
+  if (equalPos != std::string::npos) {
+    if (equalPos + 1 == getSha256DigestUriPrefix().size() &&
+        input.compare(0, getSha256DigestUriPrefix().size(), getSha256DigestUriPrefix()) == 0) {
+      return parseSha256DigestUri(std::move(input));
     }
-    catch (const StringHelperError&) {
-      BOOST_THROW_EXCEPTION(Error("Cannot convert to a ImplicitSha256DigestComponent (invalid hex "
-                                  "encoding)"));
+
+    long parsedType = std::strtol(input.data(), nullptr, 10);
+    if (parsedType < tlv::NameComponentMin || parsedType > tlv::NameComponentMax ||
+        parsedType == tlv::ImplicitSha256DigestComponent || parsedType == tlv::GenericNameComponent ||
+        to_string(parsedType).size() != equalPos) {
+      BOOST_THROW_EXCEPTION(Error("Incorrect TLV-TYPE in NameComponent URI"));
     }
+    type = static_cast<uint32_t>(parsedType);
+    input.erase(0, equalPos + 1);
   }
-  else {
-    std::string value = unescape(trimmedString);
 
-    if (value.find_first_not_of(".") == std::string::npos) {
-      // Special case for component of only periods.
-      if (value.size() <= 2)
-        // Zero, one or two periods is illegal.  Ignore this component.
-        BOOST_THROW_EXCEPTION(Error("Illegal URI (name component cannot be . or ..)"));
-      else
-        // Remove 3 periods.
-        return Component(reinterpret_cast<const uint8_t*>(&value[3]), value.size() - 3);
+  std::string value = unescape(input);
+  if (value.find_first_not_of('.') == std::string::npos) { // all periods
+    if (value.size() < 3) {
+      BOOST_THROW_EXCEPTION(Error("Illegal URI (name component cannot be . or ..)"));
     }
-    else
-      return Component(reinterpret_cast<const uint8_t*>(&value[0]), value.size());
+    return Component(type, reinterpret_cast<const uint8_t*>(value.data()), value.size() - 3);
   }
+  return Component(type, reinterpret_cast<const uint8_t*>(value.data()), value.size());
 }
 
 void
-Component::toUri(std::ostream& result) const
+Component::toUri(std::ostream& os) const
 {
   if (type() == tlv::ImplicitSha256DigestComponent) {
-    result << getSha256DigestUriPrefix();
-    printHex(result, value(), value_size(), false);
+    os << getSha256DigestUriPrefix();
+    printHex(os, value(), value_size(), false);
+    return;
   }
-  else {
-    bool hasNonDot = std::any_of(value_begin(), value_end(),
-                                 [] (uint8_t x) { return x != '.'; });
-    if (!hasNonDot) {
-      // Special case for component of zero or more periods.  Add 3 periods.
-      result << "...";
-      for (size_t i = 0; i < value_size(); ++i)
-        result << '.';
-    }
-    else {
-      escape(result, reinterpret_cast<const char*>(value()), value_size());
-    }
+
+  if (type() != tlv::GenericNameComponent) {
+    os << type() << '=';
   }
+
+  if (std::all_of(value_begin(), value_end(), [] (uint8_t x) { return x == '.'; })) { // all periods
+    os << "...";
+  }
+
+  escape(os, reinterpret_cast<const char*>(value()), value_size());
 }
 
 std::string
@@ -262,7 +274,7 @@
 Component
 Component::fromNumber(uint64_t number)
 {
-  return makeNonNegativeIntegerBlock(tlv::NameComponent, number);
+  return makeNonNegativeIntegerBlock(tlv::GenericNameComponent, number);
 }
 
 Component
@@ -274,13 +286,13 @@
   valueLength += estimator.prependByteArray(&marker, 1);
   size_t totalLength = valueLength;
   totalLength += estimator.prependVarNumber(valueLength);
-  totalLength += estimator.prependVarNumber(tlv::NameComponent);
+  totalLength += estimator.prependVarNumber(tlv::GenericNameComponent);
 
   EncodingBuffer encoder(totalLength, 0);
   encoder.prependNonNegativeInteger(number);
   encoder.prependByteArray(&marker, 1);
   encoder.prependVarNumber(valueLength);
-  encoder.prependVarNumber(tlv::NameComponent);
+  encoder.prependVarNumber(tlv::GenericNameComponent);
 
   return encoder.block();
 }
@@ -322,14 +334,14 @@
 bool
 Component::isGeneric() const
 {
-  return (type() == tlv::NameComponent);
+  return type() == tlv::GenericNameComponent;
 }
 
 bool
 Component::isImplicitSha256Digest() const
 {
-  return (type() == tlv::ImplicitSha256DigestComponent &&
-          value_size() == util::Sha256::DIGEST_SIZE);
+  return type() == tlv::ImplicitSha256DigestComponent &&
+         value_size() == util::Sha256::DIGEST_SIZE;
 }
 
 Component
diff --git a/src/name-component.hpp b/src/name-component.hpp
index c537eb1..4e3563c 100644
--- a/src/name-component.hpp
+++ b/src/name-component.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -41,15 +41,15 @@
 /// @brief Sequence number marker for NDN naming conventions
 static const uint8_t SEQUENCE_NUMBER_MARKER = 0xFE;
 
-/**
- * @brief Component holds a read-only name component value.
+/** @brief Represents a name component.
+ *
+ *  The @c Component class provides a read-only view of a @c Block interpreted as a name component.
+ *  Although it inherits mutation methods from @c Block base class, they must not be used, because
+ *  the enclosing @c Name would not be updated correctly.
  */
 class Component : public Block
 {
 public:
-  /**
-   * @brief Error that can be thrown from name::Component
-   */
   class Error : public Block::Error
   {
   public:
@@ -60,91 +60,114 @@
     }
   };
 
+public: // constructors
   /**
-   * Create a new name::Component with an empty value
+   * @brief Construct a NameComponent of @p type, using empty TLV-VALUE.
+   * @throw Error the NameComponent is invalid (see @c ensureValid).
    */
-  Component();
+  explicit
+  Component(uint32_t type = tlv::GenericNameComponent);
 
   /**
-   * @brief Create name::Component from a wire block
+   * @brief Construct a NameComponent from @p block.
+   * @throw Error the NameComponent is invalid (see @c ensureValid).
    *
-   * @param wire tlv::NameComponent Block from which to create name::Component
-   * @throws Error if wire.type() is not tlv::NameComponent
-   *
-   * Any block can be implicitly converted to name::Component
+   * This contructor enables implicit conversion from @c Block.
    */
   Component(const Block& wire);
 
   /**
-   * @brief Create a new name::Component from the buffer pointer (buffer pointer will be copied)
+   * @brief Construct a NameComponent of @p type, using TLV-VALUE from @p buffer.
+   * @throw Error the NameComponent is invalid (see @c ensureValid).
    *
-   * @param buffer A pointer to an immutable buffer
+   * This constructor does not copy the underlying buffer, but retains a pointer to it.
+   * Therefore, the caller must not change the underlying buffer.
+   */
+  Component(uint32_t type, ConstBufferPtr buffer);
+
+  /**
+   * @brief Construct a GenericNameComponent, using TLV-VALUE from @p buffer.
+   * @throw Error the NameComponent is invalid (see @c ensureValid).
    *
-   * This constructor will create a new tlv::NameComponent Block with `buffer` as a payload.
-   * Note that this method **will not** allocate new memory for and copy the payload until
-   * toWire() method is called.
+   * This constructor does not copy the underlying buffer, but retains a pointer to it.
+   * Therefore, the caller must not change the underlying buffer.
    */
   explicit
-  Component(const ConstBufferPtr& buffer);
+  Component(ConstBufferPtr buffer)
+    : Component(tlv::GenericNameComponent, std::move(buffer))
+  {
+  }
 
   /**
-   * @brief Create a new name::Component from the buffer (data from buffer will be copied)
-   * @param buffer A reference to the buffer
-   *
-   * This constructor will create a new tlv::NameComponent Block with `buffer` as a payload.
-   * Note that this method **will** allocate new memory for and copy the payload.
+   * @brief Construct a NameComponent of @p type, copying TLV-VALUE from @p buffer.
+   */
+  Component(uint32_t type, const Buffer& buffer)
+    : Component(type, buffer.data(), buffer.size())
+  {
+  }
+
+  /**
+   * @brief Construct a GenericNameComponent, copying TLV-VALUE from @p buffer.
    */
   explicit
-  Component(const Buffer& buffer);
+  Component(const Buffer& buffer)
+    : Component(tlv::GenericNameComponent, buffer)
+  {
+  }
 
   /**
-   * @brief Create a new name::Component from the buffer (data from buffer will be copied)
-   * @param buffer     A pointer to the first byte of the buffer
-   * @param bufferSize Size of the buffer
-   *
-   * This constructor will create a new tlv::NameComponent Block with `buffer` as a payload.
-   * Note that this method **will** allocate new memory for and copy the payload.
+   * @brief Construct a NameComponent of @p type, copying @p count bytes at @p value as TLV-VALUE.
    */
-  Component(const uint8_t* buffer, size_t bufferSize);
+  Component(uint32_t type, const uint8_t* value, size_t count);
 
   /**
-   * @brief Create a new name::Component frome the range [@p first, @p last) of bytes
-   * @param first     Iterator pointing to the beginning of the buffer
-   * @param last      Iterator pointing to the ending of the buffer
-   * @tparam Iterator iterator type satisfying at least InputIterator concept.  Implementation
-   *                  is more optimal when the iterator type satisfies RandomAccessIterator concept.
-   *                  It is required that sizeof(std::iterator_traits<Iterator>::value_type) == 1.
-   *
-   * This constructor will create a new tlv::NameComponent Block with `buffer` as a payload.
-   * Note that this method **will** allocate new memory for and copy the payload.
+   * @brief Construct a GenericNameComponent, copying @p count bytes at @p value as TLV-VALUE.
+   */
+  Component(const uint8_t* value, size_t count)
+    : Component(tlv::GenericNameComponent, value, count)
+  {
+  }
+
+  /**
+   * @brief Construct a NameComponent of @p type, copying TLV-VALUE from a range.
+   * @tparam Iterator an @c InputIterator dereferencing to a one-octet value type. More efficient
+   *                  implementation is available when it is a @c RandomAccessIterator.
+   * @param type      the TLV-TYPE.
+   * @param first     beginning of the range.
+   * @param last      past-end of the range.
    */
   template<class Iterator>
-  Component(Iterator first, Iterator last);
+  Component(uint32_t type, Iterator first, Iterator last)
+    : Block(makeBinaryBlock(type, first, last))
+  {
+  }
 
   /**
-   * @brief Create a new name::Component from the C string (data from string will be copied)
+   * @brief Construct a GenericNameComponent, copying TLV-VALUE from a range.
+   */
+  template<class Iterator>
+  Component(Iterator first, Iterator last)
+    : Component(tlv::GenericNameComponent, first, last)
+  {
+  }
+
+  /**
+   * @brief Construct a GenericNameComponent, copying TLV-VALUE from a null-terminated string.
    *
-   * @param str Zero-ended string.  Note that this string will be interpreted as is (i.e.,
-   *            it will not be interpreted as URI)
-   *
-   * This constructor will create a new tlv::NameComponent Block with `buffer` as a payload.
-   * Note that this method **will** allocate new memory for and copy the payload.
+   * Bytes from the string are copied as is, and not interpreted as URI component.
    */
   explicit
   Component(const char* str);
 
   /**
-   * @brief Create a new name::Component from the STL string (data from string will be copied)
+   * @brief Construct a GenericNameComponent, copying TLV-VALUE from a string.
    *
-   * @param str Const reference to STL string.  Note that this string will be interpreted
-   *            as is (i.e., it will not be interpreted as URI)
-   *
-   * This constructor will create a new tlv::NameComponent Block with `buffer` as a payload.
-   * Note that this method **will** allocate new memory for and copy the payload.
+   * Bytes from the string are copied as is, and not interpreted as URI component.
    */
   explicit
   Component(const std::string& str);
 
+public: // encoding and URI
   /**
    * @brief Fast encoding or block size estimation
    */
@@ -165,45 +188,36 @@
   wireDecode(const Block& wire);
 
   /**
-   * @brief Create name::Component by decoding the escapedString between beginOffset and
-   * endOffset according to the NDN URI Scheme.
+   * @brief Decode NameComponent from a URI component.
    *
-   * If the escaped string is "", "." or ".." then return an empty name::Component.  Note
-   * that an empty name::Component should not be added to Name and if attempted, an
-   * exception will be thrown.
+   * The URI component is read from `[input+beginOffset, input+endOffset)` range.
    *
-   * @param escapedString String containing NDN URI-encoded name
-   *                      component. [escapedString+beginOffset, beginOffset+endOffset)
-   *                      must be a valid memory buffer.
-   * @param beginOffset The offset in escapedString of the beginning of the portion to decode.
-   * @param endOffset   The offset in escapedString of the end of the portion to decode.
+   * @throw Error URI component does not represent a valid NameComponent.
    */
   static Component
-  fromEscapedString(const char* escapedString, size_t beginOffset, size_t endOffset);
-
-  /**
-   * @brief Create name::Component by decoding the escapedString according to the NDN URI Scheme
-   *
-   * This overload is a convenience wrapper for fromEscapedString(char*,size_t,size)
-   */
-  static Component
-  fromEscapedString(const char* escapedString)
+  fromEscapedString(const char* input, size_t beginOffset, size_t endOffset)
   {
-    return fromEscapedString(escapedString, 0, std::char_traits<char>::length(escapedString));
+    return fromEscapedString(std::string(input + beginOffset, input + endOffset));
   }
 
   /**
-   * @brief Create name::Component by decoding the escapedString according to the NDN URI Scheme
-   *
-   * This overload is a convenience wrapper for fromEscapedString(char*,size_t,size)
+   * @brief Decode NameComponent from a URI component.
+   * @throw Error URI component does not represent a valid NameComponent.
    */
   static Component
-  fromEscapedString(const std::string& escapedString)
+  fromEscapedString(const char* input)
   {
-    return fromEscapedString(escapedString.c_str(), 0, escapedString.size());
+    return fromEscapedString(std::string(input));
   }
 
   /**
+   * @brief Decode NameComponent from a URI component.
+   * @throw Error URI component does not represent a valid NameComponent.
+   */
+  static Component
+  fromEscapedString(std::string input);
+
+  /**
    * @brief Write *this to the output stream, escaping characters according to the NDN URI Scheme
    *
    * This also adds "..." to a value with zero or more "."
@@ -223,8 +237,7 @@
   std::string
   toUri() const;
 
-  ////////////////////////////////////////////////////////////////////////////////
-
+public: // naming conventions
   /**
    * @brief Check if the component is nonNegativeInteger
    * @see http://named-data.net/doc/ndn-tlv/tlv.html#non-negative-integer-encoding
@@ -274,8 +287,6 @@
   bool
   isSequenceNumber() const;
 
-  ////////////////////////////////////////////////////////////////////////////////
-
   /**
    * @brief Interpret this name component as nonNegativeInteger
    *
@@ -354,8 +365,6 @@
   uint64_t
   toSequenceNumber() const;
 
-  ////////////////////////////////////////////////////////////////////////////////
-
   /**
    * @brief Create a component encoded as nonNegativeInteger
    *
@@ -431,8 +440,7 @@
   static Component
   fromSequenceNumber(uint64_t seqNo);
 
-  ////////////////////////////////////////////////////////////////////////////////
-
+public: // commonly used TLV-TYPEs
   /**
    * @brief Check if the component is GenericComponent
    */
@@ -457,8 +465,7 @@
   static Component
   fromImplicitSha256Digest(const uint8_t* digest, size_t digestSize);
 
-  ////////////////////////////////////////////////////////////////////////////////
-
+public: // operators
   bool
   empty() const
   {
@@ -561,6 +568,16 @@
   Component
   getSuccessor() const;
 
+private:
+  /**
+   * @brief Throw @c Error if this NameComponent is invalid.
+   *
+   * A NameComponent is invalid if its TLV-TYPE is outside the 1-65535 range.
+   * Additionally, if this is an ImplicitSha256DigestComponent, the TLV-LENGTH must be 32.
+   */
+  void
+  ensureValid() const;
+
   // !!! NOTE TO IMPLEMENTOR !!!
   //
   // This class MUST NOT contain any data fields.
@@ -576,13 +593,6 @@
   return os;
 }
 
-template<class Iterator>
-inline
-Component::Component(Iterator first, Iterator last)
-  : Block(makeBinaryBlock(tlv::NameComponent, first, last))
-{
-}
-
 } // namespace name
 } // namespace ndn