15b607eaeSEd Tanous // SPDX-License-Identifier: Apache-2.0 25b607eaeSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 35b607eaeSEd Tanous 45b607eaeSEd Tanous #pragma once 55b607eaeSEd Tanous 65b607eaeSEd Tanous #include "logging.hpp" 75b607eaeSEd Tanous 85b607eaeSEd Tanous #include <boost/container/flat_map.hpp> 95b607eaeSEd Tanous #include <boost/container/small_vector.hpp> 105b607eaeSEd Tanous 115b607eaeSEd Tanous #include <cstddef> 125b607eaeSEd Tanous #include <format> 135b607eaeSEd Tanous #include <functional> 145b607eaeSEd Tanous #include <stdexcept> 155b607eaeSEd Tanous #include <string> 165b607eaeSEd Tanous #include <string_view> 175b607eaeSEd Tanous #include <utility> 185b607eaeSEd Tanous #include <vector> 195b607eaeSEd Tanous 205b607eaeSEd Tanous namespace crow 215b607eaeSEd Tanous { 225b607eaeSEd Tanous 235b607eaeSEd Tanous struct Node 245b607eaeSEd Tanous { 255b607eaeSEd Tanous unsigned ruleIndex = 0U; 265b607eaeSEd Tanous 275b607eaeSEd Tanous size_t stringParamChild = 0U; 285b607eaeSEd Tanous size_t pathParamChild = 0U; 295b607eaeSEd Tanous 305b607eaeSEd Tanous using ChildMap = boost::container::flat_map< 315b607eaeSEd Tanous std::string, unsigned, std::less<>, 3281f915bcSRohit PAI boost::container::small_vector<std::pair<std::string, unsigned>, 1>>; 335b607eaeSEd Tanous ChildMap children; 345b607eaeSEd Tanous isSimpleNodecrow::Node355b607eaeSEd Tanous bool isSimpleNode() const 365b607eaeSEd Tanous { 3781f915bcSRohit PAI return ruleIndex == 0 && stringParamChild == 0 && pathParamChild == 0; 385b607eaeSEd Tanous } 395b607eaeSEd Tanous }; 4081f915bcSRohit PAI template <typename ContainedType> 4181f915bcSRohit PAI class Trie 4281f915bcSRohit PAI { 4381f915bcSRohit PAI public: Trie()445b607eaeSEd Tanous Trie() : nodes(1) {} 455b607eaeSEd Tanous 465b607eaeSEd Tanous private: optimizeNode(ContainedType & node)4781f915bcSRohit PAI void optimizeNode(ContainedType& node) 485b607eaeSEd Tanous { 495b607eaeSEd Tanous if (node.stringParamChild != 0U) 505b607eaeSEd Tanous { 515b607eaeSEd Tanous optimizeNode(nodes[node.stringParamChild]); 525b607eaeSEd Tanous } 535b607eaeSEd Tanous if (node.pathParamChild != 0U) 545b607eaeSEd Tanous { 555b607eaeSEd Tanous optimizeNode(nodes[node.pathParamChild]); 565b607eaeSEd Tanous } 575b607eaeSEd Tanous 585b607eaeSEd Tanous if (node.children.empty()) 595b607eaeSEd Tanous { 605b607eaeSEd Tanous return; 615b607eaeSEd Tanous } 625b607eaeSEd Tanous while (true) 635b607eaeSEd Tanous { 645b607eaeSEd Tanous bool didMerge = false; 6581f915bcSRohit PAI typename ContainedType::ChildMap merged; 6681f915bcSRohit PAI for (const typename ContainedType::ChildMap::value_type& kv : 6781f915bcSRohit PAI node.children) 685b607eaeSEd Tanous { 6981f915bcSRohit PAI ContainedType& child = nodes[kv.second]; 705b607eaeSEd Tanous if (child.isSimpleNode()) 715b607eaeSEd Tanous { 7281f915bcSRohit PAI for (const typename ContainedType::ChildMap::value_type& 7381f915bcSRohit PAI childKv : child.children) 745b607eaeSEd Tanous { 755b607eaeSEd Tanous merged[kv.first + childKv.first] = childKv.second; 765b607eaeSEd Tanous didMerge = true; 775b607eaeSEd Tanous } 785b607eaeSEd Tanous } 795b607eaeSEd Tanous else 805b607eaeSEd Tanous { 815b607eaeSEd Tanous merged[kv.first] = kv.second; 825b607eaeSEd Tanous } 835b607eaeSEd Tanous } 845b607eaeSEd Tanous node.children = std::move(merged); 855b607eaeSEd Tanous if (!didMerge) 865b607eaeSEd Tanous { 875b607eaeSEd Tanous break; 885b607eaeSEd Tanous } 895b607eaeSEd Tanous } 905b607eaeSEd Tanous 9181f915bcSRohit PAI for (const typename ContainedType::ChildMap::value_type& kv : 9281f915bcSRohit PAI node.children) 935b607eaeSEd Tanous { 945b607eaeSEd Tanous optimizeNode(nodes[kv.second]); 955b607eaeSEd Tanous } 965b607eaeSEd Tanous } 975b607eaeSEd Tanous optimize()985b607eaeSEd Tanous void optimize() 995b607eaeSEd Tanous { 1005b607eaeSEd Tanous optimizeNode(head()); 1015b607eaeSEd Tanous } 1025b607eaeSEd Tanous 1035b607eaeSEd Tanous public: validate()1045b607eaeSEd Tanous void validate() 1055b607eaeSEd Tanous { 1065b607eaeSEd Tanous optimize(); 1075b607eaeSEd Tanous } 1085b607eaeSEd Tanous findRouteIndexesHelper(std::string_view reqUrl,std::vector<unsigned> & routeIndexes,const ContainedType & node) const1095b607eaeSEd Tanous void findRouteIndexesHelper(std::string_view reqUrl, 1105b607eaeSEd Tanous std::vector<unsigned>& routeIndexes, 11181f915bcSRohit PAI const ContainedType& node) const 1125b607eaeSEd Tanous { 11381f915bcSRohit PAI for (const typename ContainedType::ChildMap::value_type& kv : 11481f915bcSRohit PAI node.children) 1155b607eaeSEd Tanous { 1165b607eaeSEd Tanous const std::string& fragment = kv.first; 11781f915bcSRohit PAI const ContainedType& child = nodes[kv.second]; 1185b607eaeSEd Tanous if (reqUrl.empty()) 1195b607eaeSEd Tanous { 1205b607eaeSEd Tanous if (child.ruleIndex != 0 && fragment != "/") 1215b607eaeSEd Tanous { 1225b607eaeSEd Tanous routeIndexes.push_back(child.ruleIndex); 1235b607eaeSEd Tanous } 1245b607eaeSEd Tanous findRouteIndexesHelper(reqUrl, routeIndexes, child); 1255b607eaeSEd Tanous } 1265b607eaeSEd Tanous else 1275b607eaeSEd Tanous { 1285b607eaeSEd Tanous if (reqUrl.starts_with(fragment)) 1295b607eaeSEd Tanous { 1305b607eaeSEd Tanous findRouteIndexesHelper(reqUrl.substr(fragment.size()), 1315b607eaeSEd Tanous routeIndexes, child); 1325b607eaeSEd Tanous } 1335b607eaeSEd Tanous } 1345b607eaeSEd Tanous } 1355b607eaeSEd Tanous } 1365b607eaeSEd Tanous findRouteIndexes(const std::string & reqUrl,std::vector<unsigned> & routeIndexes) const1375b607eaeSEd Tanous void findRouteIndexes(const std::string& reqUrl, 1385b607eaeSEd Tanous std::vector<unsigned>& routeIndexes) const 1395b607eaeSEd Tanous { 1405b607eaeSEd Tanous findRouteIndexesHelper(reqUrl, routeIndexes, head()); 1415b607eaeSEd Tanous } 1425b607eaeSEd Tanous 1435b607eaeSEd Tanous struct FindResult 1445b607eaeSEd Tanous { 1455b607eaeSEd Tanous unsigned ruleIndex = 0; 1465b607eaeSEd Tanous std::vector<std::string> params; 1475b607eaeSEd Tanous }; 1485b607eaeSEd Tanous 1495b607eaeSEd Tanous private: findHelper(const std::string_view reqUrl,const ContainedType & node,std::vector<std::string> & params) const15081f915bcSRohit PAI FindResult findHelper(const std::string_view reqUrl, 15181f915bcSRohit PAI const ContainedType& node, 1525b607eaeSEd Tanous std::vector<std::string>& params) const 1535b607eaeSEd Tanous { 1545b607eaeSEd Tanous if (reqUrl.empty()) 1555b607eaeSEd Tanous { 1565b607eaeSEd Tanous return {node.ruleIndex, params}; 1575b607eaeSEd Tanous } 1585b607eaeSEd Tanous 1595b607eaeSEd Tanous if (node.stringParamChild != 0U) 1605b607eaeSEd Tanous { 1615b607eaeSEd Tanous size_t epos = 0; 1625b607eaeSEd Tanous for (; epos < reqUrl.size(); epos++) 1635b607eaeSEd Tanous { 1645b607eaeSEd Tanous if (reqUrl[epos] == '/') 1655b607eaeSEd Tanous { 1665b607eaeSEd Tanous break; 1675b607eaeSEd Tanous } 1685b607eaeSEd Tanous } 1695b607eaeSEd Tanous 1705b607eaeSEd Tanous if (epos != 0) 1715b607eaeSEd Tanous { 1725b607eaeSEd Tanous params.emplace_back(reqUrl.substr(0, epos)); 1735b607eaeSEd Tanous FindResult ret = findHelper( 1745b607eaeSEd Tanous reqUrl.substr(epos), nodes[node.stringParamChild], params); 1755b607eaeSEd Tanous if (ret.ruleIndex != 0U) 1765b607eaeSEd Tanous { 1775b607eaeSEd Tanous return {ret.ruleIndex, std::move(ret.params)}; 1785b607eaeSEd Tanous } 1795b607eaeSEd Tanous params.pop_back(); 1805b607eaeSEd Tanous } 1815b607eaeSEd Tanous } 1825b607eaeSEd Tanous 1835b607eaeSEd Tanous if (node.pathParamChild != 0U) 1845b607eaeSEd Tanous { 1855b607eaeSEd Tanous params.emplace_back(reqUrl); 1865b607eaeSEd Tanous FindResult ret = findHelper("", nodes[node.pathParamChild], params); 1875b607eaeSEd Tanous if (ret.ruleIndex != 0U) 1885b607eaeSEd Tanous { 1895b607eaeSEd Tanous return {ret.ruleIndex, std::move(ret.params)}; 1905b607eaeSEd Tanous } 1915b607eaeSEd Tanous params.pop_back(); 1925b607eaeSEd Tanous } 1935b607eaeSEd Tanous 19481f915bcSRohit PAI for (const typename ContainedType::ChildMap::value_type& kv : 19581f915bcSRohit PAI node.children) 1965b607eaeSEd Tanous { 1975b607eaeSEd Tanous const std::string& fragment = kv.first; 19881f915bcSRohit PAI const ContainedType& child = nodes[kv.second]; 1995b607eaeSEd Tanous 2005b607eaeSEd Tanous if (reqUrl.starts_with(fragment)) 2015b607eaeSEd Tanous { 2025b607eaeSEd Tanous FindResult ret = 2035b607eaeSEd Tanous findHelper(reqUrl.substr(fragment.size()), child, params); 2045b607eaeSEd Tanous if (ret.ruleIndex != 0U) 2055b607eaeSEd Tanous { 2065b607eaeSEd Tanous return {ret.ruleIndex, std::move(ret.params)}; 2075b607eaeSEd Tanous } 2085b607eaeSEd Tanous } 2095b607eaeSEd Tanous } 2105b607eaeSEd Tanous 2115b607eaeSEd Tanous return {0U, std::vector<std::string>()}; 2125b607eaeSEd Tanous } 2135b607eaeSEd Tanous 2145b607eaeSEd Tanous public: find(const std::string_view reqUrl) const2155b607eaeSEd Tanous FindResult find(const std::string_view reqUrl) const 2165b607eaeSEd Tanous { 2175b607eaeSEd Tanous std::vector<std::string> start; 2185b607eaeSEd Tanous return findHelper(reqUrl, head(), start); 2195b607eaeSEd Tanous } 2205b607eaeSEd Tanous add(std::string_view urlIn,unsigned ruleIndex)2215b607eaeSEd Tanous void add(std::string_view urlIn, unsigned ruleIndex) 2225b607eaeSEd Tanous { 2235b607eaeSEd Tanous size_t idx = 0; 2245b607eaeSEd Tanous 2255b607eaeSEd Tanous std::string_view url = urlIn; 2265b607eaeSEd Tanous 2275b607eaeSEd Tanous while (!url.empty()) 2285b607eaeSEd Tanous { 2295b607eaeSEd Tanous char c = url[0]; 2305b607eaeSEd Tanous if (c == '<') 2315b607eaeSEd Tanous { 2325b607eaeSEd Tanous bool found = false; 2335b607eaeSEd Tanous for (const std::string_view str1 : 2345b607eaeSEd Tanous {"<str>", "<string>", "<path>"}) 2355b607eaeSEd Tanous { 2365b607eaeSEd Tanous if (!url.starts_with(str1)) 2375b607eaeSEd Tanous { 2385b607eaeSEd Tanous continue; 2395b607eaeSEd Tanous } 2405b607eaeSEd Tanous found = true; 24181f915bcSRohit PAI ContainedType& node = nodes[idx]; 2425b607eaeSEd Tanous size_t* param = &node.stringParamChild; 2435b607eaeSEd Tanous if (str1 == "<path>") 2445b607eaeSEd Tanous { 2455b607eaeSEd Tanous param = &node.pathParamChild; 2465b607eaeSEd Tanous } 2475b607eaeSEd Tanous if (*param == 0U) 2485b607eaeSEd Tanous { 2495b607eaeSEd Tanous *param = newNode(); 2505b607eaeSEd Tanous } 2515b607eaeSEd Tanous idx = *param; 2525b607eaeSEd Tanous 2535b607eaeSEd Tanous url.remove_prefix(str1.size()); 2545b607eaeSEd Tanous break; 2555b607eaeSEd Tanous } 2565b607eaeSEd Tanous if (found) 2575b607eaeSEd Tanous { 2585b607eaeSEd Tanous continue; 2595b607eaeSEd Tanous } 2605b607eaeSEd Tanous 2615b607eaeSEd Tanous BMCWEB_LOG_CRITICAL("Can't find tag for {}", urlIn); 2625b607eaeSEd Tanous return; 2635b607eaeSEd Tanous } 2645b607eaeSEd Tanous std::string piece(&c, 1); 2655b607eaeSEd Tanous if (!nodes[idx].children.contains(piece)) 2665b607eaeSEd Tanous { 2675b607eaeSEd Tanous unsigned newNodeIdx = newNode(); 2685b607eaeSEd Tanous nodes[idx].children.emplace(piece, newNodeIdx); 2695b607eaeSEd Tanous } 2705b607eaeSEd Tanous idx = nodes[idx].children[piece]; 2715b607eaeSEd Tanous url.remove_prefix(1); 2725b607eaeSEd Tanous } 27381f915bcSRohit PAI ContainedType& node = nodes[idx]; 2745b607eaeSEd Tanous if (node.ruleIndex != 0U) 2755b607eaeSEd Tanous { 2765b607eaeSEd Tanous BMCWEB_LOG_CRITICAL("handler already exists for \"{}\"", urlIn); 2775b607eaeSEd Tanous throw std::runtime_error( 2785b607eaeSEd Tanous std::format("handler already exists for \"{}\"", urlIn)); 2795b607eaeSEd Tanous } 2805b607eaeSEd Tanous node.ruleIndex = ruleIndex; 2815b607eaeSEd Tanous } 2825b607eaeSEd Tanous 2835b607eaeSEd Tanous private: debugNodePrint(ContainedType & n,size_t level)28481f915bcSRohit PAI void debugNodePrint(ContainedType& n, size_t level) 2855b607eaeSEd Tanous { 2865b607eaeSEd Tanous std::string spaces(level, ' '); 2875b607eaeSEd Tanous if (n.stringParamChild != 0U) 2885b607eaeSEd Tanous { 2895b607eaeSEd Tanous BMCWEB_LOG_DEBUG("{}<str>", spaces); 2905b607eaeSEd Tanous debugNodePrint(nodes[n.stringParamChild], level + 5); 2915b607eaeSEd Tanous } 2925b607eaeSEd Tanous if (n.pathParamChild != 0U) 2935b607eaeSEd Tanous { 2945b607eaeSEd Tanous BMCWEB_LOG_DEBUG("{} <path>", spaces); 2955b607eaeSEd Tanous debugNodePrint(nodes[n.pathParamChild], level + 6); 2965b607eaeSEd Tanous } 29781f915bcSRohit PAI for (const typename ContainedType::ChildMap::value_type& kv : 29881f915bcSRohit PAI n.children) 2995b607eaeSEd Tanous { 3005b607eaeSEd Tanous BMCWEB_LOG_DEBUG("{}{}", spaces, kv.first); 3015b607eaeSEd Tanous debugNodePrint(nodes[kv.second], level + kv.first.size()); 3025b607eaeSEd Tanous } 3035b607eaeSEd Tanous } 3045b607eaeSEd Tanous 3055b607eaeSEd Tanous public: debugPrint()3065b607eaeSEd Tanous void debugPrint() 3075b607eaeSEd Tanous { 3085b607eaeSEd Tanous debugNodePrint(head(), 0U); 3095b607eaeSEd Tanous } 3105b607eaeSEd Tanous 311*c1a75ebcSrohitpai protected: head() const31281f915bcSRohit PAI const ContainedType& head() const 3135b607eaeSEd Tanous { 3145b607eaeSEd Tanous return nodes.front(); 3155b607eaeSEd Tanous } 3165b607eaeSEd Tanous head()31781f915bcSRohit PAI ContainedType& head() 3185b607eaeSEd Tanous { 3195b607eaeSEd Tanous return nodes.front(); 3205b607eaeSEd Tanous } 3215b607eaeSEd Tanous newNode()3225b607eaeSEd Tanous unsigned newNode() 3235b607eaeSEd Tanous { 3245b607eaeSEd Tanous nodes.resize(nodes.size() + 1); 3255b607eaeSEd Tanous return static_cast<unsigned>(nodes.size() - 1); 3265b607eaeSEd Tanous } 3275b607eaeSEd Tanous 32881f915bcSRohit PAI std::vector<ContainedType> nodes{}; 3295b607eaeSEd Tanous }; 3305b607eaeSEd Tanous } // namespace crow 331