Refactor getParameterTag

The aforementioned method is recursive, which prevents us from enabling
recursive checks.   This is from back in the days when constexpr
string_view didn't exist, and recursion was the only way to do string
parsing.  These days, we are much more evolved, so simplify the method.

Tested: Code compiles (this is a constexpr method) and unit tests pass

Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I3b8364ac38a14faf546edb85deae7071f7558f4b
diff --git a/http/utility.hpp b/http/utility.hpp
index 07fc1ee..52a72b6 100644
--- a/http/utility.hpp
+++ b/http/utility.hpp
@@ -24,38 +24,8 @@
 namespace black_magic
 {
 
-constexpr unsigned findClosingTag(std::string_view s, unsigned p)
-{
-    return s[p] == '>' ? p : findClosingTag(s, p + 1);
-}
-
-constexpr bool isInt(std::string_view s, unsigned i)
-{
-    return s.substr(i, 5) == "<int>";
-}
-
-constexpr bool isUint(std::string_view s, unsigned i)
-{
-    return s.substr(i, 6) == "<uint>";
-}
-
-constexpr bool isFloat(std::string_view s, unsigned i)
-{
-    return s.substr(i, 7) == "<float>" || s.substr(i, 8) == "<double>";
-}
-
-constexpr bool isStr(std::string_view s, unsigned i)
-{
-    return s.substr(i, 5) == "<str>" || s.substr(i, 8) == "<string>";
-}
-
-constexpr bool isPath(std::string_view s, unsigned i)
-{
-    return s.substr(i, 6) == "<path>";
-}
-
 template <typename T>
-constexpr int getParameterTag()
+constexpr uint64_t getParameterTag()
 {
     if constexpr (std::is_same_v<int, T>)
     {
@@ -123,78 +93,109 @@
     static constexpr int subValue =
         computeParameterTagFromArgsList<Args...>::value;
     static constexpr int value =
-        getParameterTag<typename std::decay<Arg>::type>()
+        getParameterTag<typename std::decay<Arg>::type>() != 0
             ? subValue * 6 + getParameterTag<typename std::decay<Arg>::type>()
             : subValue;
 };
 
 inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
 {
-
-    if (a == 0)
+    while (true)
     {
-        return b == 0;
+        if (a == 0)
+        {
+            return b == 0;
+        }
+        if (b == 0)
+        {
+            return a == 0;
+        }
+        uint64_t sa = a % 6;
+        uint64_t sb = a % 6;
+        if (sa == 5)
+        {
+            sa = 4;
+        }
+        if (sb == 5)
+        {
+            sb = 4;
+        }
+        if (sa != sb)
+        {
+            return false;
+        }
+        a /= 6;
+        b /= 6;
     }
-    if (b == 0)
-    {
-        return a == 0;
-    }
-    uint64_t sa = a % 6;
-    uint64_t sb = a % 6;
-    if (sa == 5)
-    {
-        sa = 4;
-    }
-    if (sb == 5)
-    {
-        sb = 4;
-    }
-    if (sa != sb)
-    {
-        return false;
-    }
-    return isParameterTagCompatible(a / 6, b / 6);
+    return false;
 }
 
-constexpr uint64_t getParameterTag(std::string_view s, unsigned p = 0)
+constexpr inline uint64_t getParameterTag(std::string_view url)
 {
+    uint64_t tagValue = 0;
+    size_t urlSegmentIndex = std::string_view::npos;
 
-    if (p == s.size())
+    size_t paramIndex = 0;
+
+    for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
+    {
+        char character = url[urlIndex];
+        if (character == '<')
+        {
+            if (urlSegmentIndex != std::string_view::npos)
+            {
+                return 0;
+            }
+            urlSegmentIndex = urlIndex;
+        }
+        if (character == '>')
+        {
+            if (urlSegmentIndex == std::string_view::npos)
+            {
+                return 0;
+            }
+            std::string_view tag =
+                url.substr(urlSegmentIndex, urlIndex + 1 - urlSegmentIndex);
+
+            // Note, this is a really lame way to do std::pow(6, paramIndex)
+            // std::pow doesn't work in constexpr in clang.
+            // Ideally in the future we'd move this to use a power of 2 packing
+            // (probably 8 instead of 6) so that these just become bit shifts
+            uint64_t insertIndex = 1;
+            for (size_t unused = 0; unused < paramIndex; unused++)
+            {
+                insertIndex *= 6;
+            }
+
+            if (tag == "<int>")
+            {
+                tagValue += insertIndex * 1;
+            }
+            if (tag == "<uint>")
+            {
+                tagValue += insertIndex * 2;
+            }
+            if (tag == "<float>" || tag == "<double>")
+            {
+                tagValue += insertIndex * 3;
+            }
+            if (tag == "<str>" || tag == "<string>")
+            {
+                tagValue += insertIndex * 4;
+            }
+            if (tag == "<path>")
+            {
+                tagValue += insertIndex * 5;
+            }
+            paramIndex++;
+            urlSegmentIndex = std::string_view::npos;
+        }
+    }
+    if (urlSegmentIndex != std::string_view::npos)
     {
         return 0;
     }
-
-    if (s[p] != '<')
-    {
-        return getParameterTag(s, p + 1);
-    }
-
-    if (isInt(s, p))
-    {
-        return getParameterTag(s, findClosingTag(s, p)) * 6 + 1;
-    }
-
-    if (isUint(s, p))
-    {
-        return getParameterTag(s, findClosingTag(s, p)) * 6 + 2;
-    }
-
-    if (isFloat(s, p))
-    {
-        return getParameterTag(s, findClosingTag(s, p)) * 6 + 3;
-    }
-
-    if (isStr(s, p))
-    {
-        return getParameterTag(s, findClosingTag(s, p)) * 6 + 4;
-    }
-
-    if (isPath(s, p))
-    {
-        return getParameterTag(s, findClosingTag(s, p)) * 6 + 5;
-    }
-
-    throw std::runtime_error("invalid parameter type");
+    return tagValue;
 }
 
 template <typename... T>