blob: bfa441a4416c5c9e245cb3f6b1c1cd45a3d9abf8 [file] [log] [blame]
Ed Tanous5b607ea2025-03-26 12:13:39 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
3
4#pragma once
5
6#include "logging.hpp"
7
8#include <boost/container/flat_map.hpp>
9#include <boost/container/small_vector.hpp>
10
11#include <cstddef>
12#include <format>
13#include <functional>
14#include <stdexcept>
15#include <string>
16#include <string_view>
17#include <utility>
18#include <vector>
19
20namespace crow
21{
22
Rohit PAI81f915b2025-04-02 12:29:29 +053023struct Node
24{
25 unsigned ruleIndex = 0U;
26
27 size_t stringParamChild = 0U;
28 size_t pathParamChild = 0U;
29
30 using ChildMap = boost::container::flat_map<
31 std::string, unsigned, std::less<>,
32 boost::container::small_vector<std::pair<std::string, unsigned>, 1>>;
33 ChildMap children;
34
35 bool isSimpleNode() const
36 {
37 return ruleIndex == 0 && stringParamChild == 0 && pathParamChild == 0;
38 }
39};
Ed Tanous5b607ea2025-03-26 12:13:39 -070040template <typename ContainedType>
41class Trie
42{
43 public:
Ed Tanous5b607ea2025-03-26 12:13:39 -070044 Trie() : nodes(1) {}
45
46 private:
Rohit PAI81f915b2025-04-02 12:29:29 +053047 void optimizeNode(ContainedType& node)
Ed Tanous5b607ea2025-03-26 12:13:39 -070048 {
49 if (node.stringParamChild != 0U)
50 {
51 optimizeNode(nodes[node.stringParamChild]);
52 }
53 if (node.pathParamChild != 0U)
54 {
55 optimizeNode(nodes[node.pathParamChild]);
56 }
57
58 if (node.children.empty())
59 {
60 return;
61 }
62 while (true)
63 {
64 bool didMerge = false;
Rohit PAI81f915b2025-04-02 12:29:29 +053065 typename ContainedType::ChildMap merged;
66 for (const typename ContainedType::ChildMap::value_type& kv :
67 node.children)
Ed Tanous5b607ea2025-03-26 12:13:39 -070068 {
Rohit PAI81f915b2025-04-02 12:29:29 +053069 ContainedType& child = nodes[kv.second];
Ed Tanous5b607ea2025-03-26 12:13:39 -070070 if (child.isSimpleNode())
71 {
Rohit PAI81f915b2025-04-02 12:29:29 +053072 for (const typename ContainedType::ChildMap::value_type&
73 childKv : child.children)
Ed Tanous5b607ea2025-03-26 12:13:39 -070074 {
75 merged[kv.first + childKv.first] = childKv.second;
76 didMerge = true;
77 }
78 }
79 else
80 {
81 merged[kv.first] = kv.second;
82 }
83 }
84 node.children = std::move(merged);
85 if (!didMerge)
86 {
87 break;
88 }
89 }
90
Rohit PAI81f915b2025-04-02 12:29:29 +053091 for (const typename ContainedType::ChildMap::value_type& kv :
92 node.children)
Ed Tanous5b607ea2025-03-26 12:13:39 -070093 {
94 optimizeNode(nodes[kv.second]);
95 }
96 }
97
98 void optimize()
99 {
100 optimizeNode(head());
101 }
102
103 public:
104 void validate()
105 {
106 optimize();
107 }
108
109 void findRouteIndexesHelper(std::string_view reqUrl,
110 std::vector<unsigned>& routeIndexes,
Rohit PAI81f915b2025-04-02 12:29:29 +0530111 const ContainedType& node) const
Ed Tanous5b607ea2025-03-26 12:13:39 -0700112 {
Rohit PAI81f915b2025-04-02 12:29:29 +0530113 for (const typename ContainedType::ChildMap::value_type& kv :
114 node.children)
Ed Tanous5b607ea2025-03-26 12:13:39 -0700115 {
116 const std::string& fragment = kv.first;
Rohit PAI81f915b2025-04-02 12:29:29 +0530117 const ContainedType& child = nodes[kv.second];
Ed Tanous5b607ea2025-03-26 12:13:39 -0700118 if (reqUrl.empty())
119 {
120 if (child.ruleIndex != 0 && fragment != "/")
121 {
122 routeIndexes.push_back(child.ruleIndex);
123 }
124 findRouteIndexesHelper(reqUrl, routeIndexes, child);
125 }
126 else
127 {
128 if (reqUrl.starts_with(fragment))
129 {
130 findRouteIndexesHelper(reqUrl.substr(fragment.size()),
131 routeIndexes, child);
132 }
133 }
134 }
135 }
136
137 void findRouteIndexes(const std::string& reqUrl,
138 std::vector<unsigned>& routeIndexes) const
139 {
140 findRouteIndexesHelper(reqUrl, routeIndexes, head());
141 }
142
143 struct FindResult
144 {
145 unsigned ruleIndex = 0;
146 std::vector<std::string> params;
147 };
148
149 private:
Rohit PAI81f915b2025-04-02 12:29:29 +0530150 FindResult findHelper(const std::string_view reqUrl,
151 const ContainedType& node,
Ed Tanous5b607ea2025-03-26 12:13:39 -0700152 std::vector<std::string>& params) const
153 {
154 if (reqUrl.empty())
155 {
156 return {node.ruleIndex, params};
157 }
158
159 if (node.stringParamChild != 0U)
160 {
161 size_t epos = 0;
162 for (; epos < reqUrl.size(); epos++)
163 {
164 if (reqUrl[epos] == '/')
165 {
166 break;
167 }
168 }
169
170 if (epos != 0)
171 {
172 params.emplace_back(reqUrl.substr(0, epos));
173 FindResult ret = findHelper(
174 reqUrl.substr(epos), nodes[node.stringParamChild], params);
175 if (ret.ruleIndex != 0U)
176 {
177 return {ret.ruleIndex, std::move(ret.params)};
178 }
179 params.pop_back();
180 }
181 }
182
183 if (node.pathParamChild != 0U)
184 {
185 params.emplace_back(reqUrl);
186 FindResult ret = findHelper("", nodes[node.pathParamChild], params);
187 if (ret.ruleIndex != 0U)
188 {
189 return {ret.ruleIndex, std::move(ret.params)};
190 }
191 params.pop_back();
192 }
193
Rohit PAI81f915b2025-04-02 12:29:29 +0530194 for (const typename ContainedType::ChildMap::value_type& kv :
195 node.children)
Ed Tanous5b607ea2025-03-26 12:13:39 -0700196 {
197 const std::string& fragment = kv.first;
Rohit PAI81f915b2025-04-02 12:29:29 +0530198 const ContainedType& child = nodes[kv.second];
Ed Tanous5b607ea2025-03-26 12:13:39 -0700199
200 if (reqUrl.starts_with(fragment))
201 {
202 FindResult ret =
203 findHelper(reqUrl.substr(fragment.size()), child, params);
204 if (ret.ruleIndex != 0U)
205 {
206 return {ret.ruleIndex, std::move(ret.params)};
207 }
208 }
209 }
210
211 return {0U, std::vector<std::string>()};
212 }
213
214 public:
215 FindResult find(const std::string_view reqUrl) const
216 {
217 std::vector<std::string> start;
218 return findHelper(reqUrl, head(), start);
219 }
220
221 void add(std::string_view urlIn, unsigned ruleIndex)
222 {
223 size_t idx = 0;
224
225 std::string_view url = urlIn;
226
227 while (!url.empty())
228 {
229 char c = url[0];
230 if (c == '<')
231 {
232 bool found = false;
233 for (const std::string_view str1 :
234 {"<str>", "<string>", "<path>"})
235 {
236 if (!url.starts_with(str1))
237 {
238 continue;
239 }
240 found = true;
Rohit PAI81f915b2025-04-02 12:29:29 +0530241 ContainedType& node = nodes[idx];
Ed Tanous5b607ea2025-03-26 12:13:39 -0700242 size_t* param = &node.stringParamChild;
243 if (str1 == "<path>")
244 {
245 param = &node.pathParamChild;
246 }
247 if (*param == 0U)
248 {
249 *param = newNode();
250 }
251 idx = *param;
252
253 url.remove_prefix(str1.size());
254 break;
255 }
256 if (found)
257 {
258 continue;
259 }
260
261 BMCWEB_LOG_CRITICAL("Can't find tag for {}", urlIn);
262 return;
263 }
264 std::string piece(&c, 1);
265 if (!nodes[idx].children.contains(piece))
266 {
267 unsigned newNodeIdx = newNode();
268 nodes[idx].children.emplace(piece, newNodeIdx);
269 }
270 idx = nodes[idx].children[piece];
271 url.remove_prefix(1);
272 }
Rohit PAI81f915b2025-04-02 12:29:29 +0530273 ContainedType& node = nodes[idx];
Ed Tanous5b607ea2025-03-26 12:13:39 -0700274 if (node.ruleIndex != 0U)
275 {
276 BMCWEB_LOG_CRITICAL("handler already exists for \"{}\"", urlIn);
277 throw std::runtime_error(
278 std::format("handler already exists for \"{}\"", urlIn));
279 }
280 node.ruleIndex = ruleIndex;
281 }
282
283 private:
Rohit PAI81f915b2025-04-02 12:29:29 +0530284 void debugNodePrint(ContainedType& n, size_t level)
Ed Tanous5b607ea2025-03-26 12:13:39 -0700285 {
286 std::string spaces(level, ' ');
287 if (n.stringParamChild != 0U)
288 {
289 BMCWEB_LOG_DEBUG("{}<str>", spaces);
290 debugNodePrint(nodes[n.stringParamChild], level + 5);
291 }
292 if (n.pathParamChild != 0U)
293 {
294 BMCWEB_LOG_DEBUG("{} <path>", spaces);
295 debugNodePrint(nodes[n.pathParamChild], level + 6);
296 }
Rohit PAI81f915b2025-04-02 12:29:29 +0530297 for (const typename ContainedType::ChildMap::value_type& kv :
298 n.children)
Ed Tanous5b607ea2025-03-26 12:13:39 -0700299 {
300 BMCWEB_LOG_DEBUG("{}{}", spaces, kv.first);
301 debugNodePrint(nodes[kv.second], level + kv.first.size());
302 }
303 }
304
305 public:
306 void debugPrint()
307 {
308 debugNodePrint(head(), 0U);
309 }
310
rohitpaic1a75eb2025-01-03 19:13:36 +0530311 protected:
Rohit PAI81f915b2025-04-02 12:29:29 +0530312 const ContainedType& head() const
Ed Tanous5b607ea2025-03-26 12:13:39 -0700313 {
314 return nodes.front();
315 }
316
Rohit PAI81f915b2025-04-02 12:29:29 +0530317 ContainedType& head()
Ed Tanous5b607ea2025-03-26 12:13:39 -0700318 {
319 return nodes.front();
320 }
321
322 unsigned newNode()
323 {
324 nodes.resize(nodes.size() + 1);
325 return static_cast<unsigned>(nodes.size() - 1);
326 }
327
Rohit PAI81f915b2025-04-02 12:29:29 +0530328 std::vector<ContainedType> nodes{};
Ed Tanous5b607ea2025-03-26 12:13:39 -0700329};
330} // namespace crow