name-component: recognize typed name components

refs #4526

Change-Id: I9d32a5dc216c6b0921d181573a4e043fccc2814e
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