name-component: recognize plain URI syntax

refs #4690

Change-Id: I6e88d88cc62a025edc30b545f1044a225c20213f
diff --git a/src/name-component.cpp b/src/name-component.cpp
index 7252d29..6ac6f31 100644
--- a/src/name-component.cpp
+++ b/src/name-component.cpp
@@ -24,12 +24,9 @@
  */
 
 #include "name-component.hpp"
+#include "detail/name-component-types.hpp"
 
-#include "encoding/block-helpers.hpp"
-#include "encoding/encoding-buffer.hpp"
-#include "util/sha256.hpp"
-#include "util/string-helper.hpp"
-
+#include <cstdlib>
 #include <cstring>
 #include <sstream>
 
@@ -43,23 +40,13 @@
 static_assert(std::is_base_of<tlv::Error, Component::Error>::value,
               "name::Component::Error must inherit from tlv::Error");
 
-static const std::string&
-getSha256DigestUriPrefix()
-{
-  static const std::string prefix{"sha256digest="};
-  return prefix;
-}
-
 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)));
-  }
+  detail::getComponentTypeTable().get(type()).check(*this);
 }
 
 Component::Component(uint32_t type)
@@ -77,11 +64,13 @@
 Component::Component(uint32_t type, ConstBufferPtr buffer)
   : Block(type, std::move(buffer))
 {
+  ensureValid();
 }
 
 Component::Component(uint32_t type, const uint8_t* value, size_t valueLen)
   : Block(makeBinaryBlock(type, value, valueLen))
 {
+  ensureValid();
 }
 
 Component::Component(const char* str)
@@ -95,68 +84,48 @@
 }
 
 static Component
-parseSha256DigestUri(std::string input)
+parseUriEscapedValue(uint32_t type, const char* input, size_t len)
 {
-  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(std::string 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));
-    }
-
-    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);
-  }
-
-  std::string value = unescape(input);
+  std::ostringstream oss;
+  unescape(oss, input, len);
+  std::string value = oss.str();
   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 ..)"));
+      BOOST_THROW_EXCEPTION(Component::Error("Illegal URI (name component cannot be . or ..)"));
     }
     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());
 }
 
+Component
+Component::fromEscapedString(const std::string& input)
+{
+  size_t equalPos = input.find('=');
+  if (equalPos == std::string::npos) {
+    return parseUriEscapedValue(tlv::GenericNameComponent, input.data(), input.size());
+  }
+
+  long type = std::strtol(input.data(), nullptr, 10);
+  if (type >= tlv::NameComponentMin && type <= tlv::NameComponentMax &&
+      to_string(type).size() == equalPos) {
+    size_t valuePos = equalPos + 1;
+    return parseUriEscapedValue(static_cast<uint32_t>(type), input.data() + valuePos,
+                                input.size() - valuePos);
+  }
+
+  auto typePrefix = input.substr(0, equalPos);
+  auto ct = detail::getComponentTypeTable().findByUriPrefix(typePrefix);
+  if (ct == nullptr) {
+    BOOST_THROW_EXCEPTION(Error("Incorrect TLV-TYPE '" + typePrefix + "' in NameComponent URI"));
+  }
+  return ct->parseAltUriValue(input.substr(equalPos + 1));
+}
+
 void
 Component::toUri(std::ostream& os) const
 {
-  if (type() == tlv::ImplicitSha256DigestComponent) {
-    os << getSha256DigestUriPrefix();
-    printHex(os, value(), value_size(), false);
-    return;
-  }
-
-  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());
+  detail::getComponentTypeTable().get(type()).writeUri(os, *this);
 }
 
 std::string
@@ -398,59 +367,20 @@
   return std::memcmp(value(), other.value(), value_size());
 }
 
-static Component
-getDigestSuccessor(const Component& comp)
-{
-  size_t totalLength = 0;
-  EncodingBuffer encoder(comp.size(), 0);
-
-  bool isOverflow = true;
-  size_t i = comp.value_size();
-  for (; isOverflow && i > 0; i--) {
-    uint8_t newValue = static_cast<uint8_t>((comp.value()[i - 1] + 1) & 0xFF);
-    totalLength += encoder.prependByte(newValue);
-    isOverflow = (newValue == 0);
-  }
-  totalLength += encoder.prependByteArray(comp.value(), i);
-
-  if (isOverflow) {
-    return Component(comp.type() + 1);
-  }
-
-  encoder.prependVarNumber(totalLength);
-  encoder.prependVarNumber(comp.type());
-  return encoder.block();
-}
-
 Component
 Component::getSuccessor() const
 {
-  if (isImplicitSha256Digest()) {
-    return getDigestSuccessor(*this);
+  bool isOverflow = false;
+  Component successor;
+  std::tie(isOverflow, successor) =
+    detail::getComponentTypeTable().get(type()).getSuccessor(*this);
+  if (!isOverflow) {
+    return successor;
   }
 
-  size_t totalLength = 0;
-  EncodingBuffer encoder(size() + 9, 9);
-  // leave room for additional byte when TLV-VALUE overflows, and for TLV-LENGTH size increase
-
-  bool isOverflow = true;
-  size_t i = value_size();
-  for (; isOverflow && i > 0; i--) {
-    uint8_t newValue = static_cast<uint8_t>((value()[i - 1] + 1) & 0xFF);
-    totalLength += encoder.prependByte(newValue);
-    isOverflow = (newValue == 0);
-  }
-  totalLength += encoder.prependByteArray(value(), i);
-
-  if (isOverflow) {
-    // new name component has to be extended
-    totalLength += encoder.appendByte(0);
-  }
-
-  encoder.prependVarNumber(totalLength);
-  encoder.prependVarNumber(type());
-
-  return encoder.block();
+  uint32_t type = this->type() + 1;
+  const std::vector<uint8_t>& value = detail::getComponentTypeTable().get(type).getMinValue();
+  return Component(type, value.data(), value.size());
 }
 
 template<encoding::Tag TAG>