be/src/util/path_trie.hpp
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #pragma once |
19 | | |
20 | | #include <map> |
21 | | #include <memory> |
22 | | #include <string> |
23 | | #include <vector> |
24 | | |
25 | | namespace doris { |
26 | | |
27 | | // This tree is usd for manage restful api path. |
28 | | template <class T> |
29 | | class PathTrie { |
30 | | public: |
31 | 60 | PathTrie() : _root("/", "*"), _root_value(nullptr), _separator('/') {} |
32 | | |
33 | 36 | ~PathTrie() { |
34 | 36 | if (_root_value != nullptr) { |
35 | 4 | _allocator.destroy(_root_value); |
36 | 4 | _allocator.deallocate(_root_value, 1); |
37 | 4 | } |
38 | 36 | } |
39 | | |
40 | | class Allocator { |
41 | | public: |
42 | | using value_type = T; |
43 | | |
44 | 641 | T* allocate(size_t n) { return static_cast<T*>(::operator new(sizeof(T) * n)); } |
45 | | |
46 | | template <typename... Args> |
47 | 6.77k | void construct(T* p, Args&&... args) { |
48 | 6.77k | new (p) T(std::forward<Args>(args)...); |
49 | 6.77k | } _ZN5doris8PathTrieIPNS_11HttpHandlerEE9Allocator9constructIJRKS2_EEEvPS2_DpOT_ Line | Count | Source | 47 | 641 | void construct(T* p, Args&&... args) { | 48 | 641 | new (p) T(std::forward<Args>(args)...); | 49 | 641 | } |
_ZN5doris8PathTrieIPNS_11HttpHandlerEE9Allocator9constructIJRS2_EEEvPS2_DpOT_ Line | Count | Source | 47 | 6.13k | void construct(T* p, Args&&... args) { | 48 | 6.13k | new (p) T(std::forward<Args>(args)...); | 49 | 6.13k | } |
|
50 | | |
51 | 320 | void destroy(T* p) { p->~T(); } |
52 | | |
53 | 320 | void deallocate(T* p, size_t n) { ::operator delete(p); } |
54 | | }; |
55 | | |
56 | | class TrieNode { |
57 | | public: |
58 | | TrieNode(const std::string& key, const std::string& wildcard) |
59 | 344 | : _value(nullptr), _wildcard(wildcard) { |
60 | 344 | if (is_named_wildcard(key)) { |
61 | 40 | _named_wildcard = extract_template(key); |
62 | 40 | } |
63 | 344 | } |
64 | | |
65 | | TrieNode(const std::string& key, const T& value, const std::string& wildcard) |
66 | 633 | : _value(nullptr), _wildcard(wildcard) { |
67 | 633 | _value = _allocator.allocate(1); |
68 | 633 | _allocator.construct(_value, value); |
69 | 633 | if (is_named_wildcard(key)) { |
70 | 80 | _named_wildcard = extract_template(key); |
71 | 80 | } |
72 | 633 | } |
73 | | |
74 | 498 | ~TrieNode() { |
75 | 498 | for (auto& iter : _children) { |
76 | 462 | delete iter.second; |
77 | 462 | iter.second = nullptr; |
78 | 462 | } |
79 | 498 | if (_value != nullptr) { |
80 | 316 | _allocator.destroy(_value); |
81 | 316 | _allocator.deallocate(_value, 1); |
82 | 316 | } |
83 | 498 | } |
84 | | |
85 | | // Return true if insert success. |
86 | 1.51k | bool insert(const std::vector<std::string> path, int index, const T& value) { |
87 | 1.51k | if (index >= path.size()) { |
88 | 0 | return false; |
89 | 0 | } |
90 | 1.51k | const std::string& token = path[index]; |
91 | 1.51k | std::string key = token; |
92 | | |
93 | 1.51k | if (is_named_wildcard(token)) { |
94 | 176 | key = _wildcard; |
95 | 176 | } |
96 | | |
97 | 1.51k | TrieNode* node = get_child(key); |
98 | | |
99 | 1.51k | if (node == nullptr) { |
100 | | // no exist child for this key |
101 | 917 | if (index == path.size() - 1) { |
102 | 633 | node = new TrieNode(token, value, _wildcard); |
103 | 633 | _children.insert(std::make_pair(key, node)); |
104 | 633 | return true; |
105 | 633 | } else { |
106 | 284 | node = new TrieNode(token, _wildcard); |
107 | 284 | _children.insert(std::make_pair(key, node)); |
108 | 284 | } |
109 | 917 | } else { |
110 | | // If this is a template, set this to the node |
111 | 597 | if (is_named_wildcard(token)) { |
112 | 56 | std::string temp = extract_template(token); |
113 | 56 | if (node->_named_wildcard.empty() || node->_named_wildcard.compare(temp) == 0) { |
114 | 56 | node->_named_wildcard = temp; |
115 | 56 | } else { |
116 | | // Duplicated |
117 | 0 | return false; |
118 | 0 | } |
119 | 56 | } |
120 | 597 | if (index == path.size() - 1) { |
121 | 0 | if (node->_value == nullptr) { |
122 | 0 | node->_value = _allocator.allocate(1); |
123 | 0 | _allocator.construct(node->_value, value); |
124 | 0 | return true; |
125 | 0 | } |
126 | | // Already register by other path |
127 | 0 | return false; |
128 | 0 | } |
129 | 597 | } |
130 | 881 | return node->insert(path, index + 1, value); |
131 | 1.51k | } |
132 | | |
133 | | bool retrieve(const std::vector<std::string> path, int index, T* value, |
134 | 20.2k | std::map<std::string, std::string>* params) { |
135 | | // check max index |
136 | 20.2k | if (index >= path.size()) { |
137 | 0 | return false; |
138 | 0 | } |
139 | 20.2k | bool use_wildcard = false; |
140 | 20.2k | const std::string& token = path[index]; |
141 | 20.2k | TrieNode* node = get_child(token); |
142 | 20.2k | if (node == nullptr) { |
143 | 4.34k | node = get_child(_wildcard); |
144 | 4.34k | if (node == nullptr) { |
145 | 3 | return false; |
146 | 3 | } |
147 | 4.34k | use_wildcard = true; |
148 | 15.8k | } else { |
149 | | // If we the last one, but we have no value, check wildcard |
150 | 15.8k | if (index == path.size() - 1 && node->_value == nullptr && |
151 | 15.8k | get_child(_wildcard) != nullptr) { |
152 | 0 | node = get_child(_wildcard); |
153 | 0 | use_wildcard = true; |
154 | 15.8k | } else { |
155 | 15.8k | use_wildcard = (token.compare(_wildcard) == 0); |
156 | 15.8k | } |
157 | 15.8k | } |
158 | | |
159 | 20.2k | put(params, node, token); |
160 | | |
161 | 20.2k | if (index == path.size() - 1) { |
162 | 6.13k | if (node->_value == nullptr) { |
163 | 0 | return false; |
164 | 0 | } |
165 | 6.13k | _allocator.construct(value, *node->_value); |
166 | 6.13k | return true; |
167 | 6.13k | } |
168 | | |
169 | | // find exact |
170 | 14.1k | if (node->retrieve(path, index + 1, value, params)) { |
171 | 14.1k | return true; |
172 | 14.1k | } |
173 | | |
174 | | // backtrace to test if wildcard can match |
175 | 2 | if (!use_wildcard) { |
176 | 2 | node = get_child(_wildcard); |
177 | 2 | if (node != nullptr) { |
178 | 0 | put(params, node, token); |
179 | 0 | return node->retrieve(path, index + 1, value, params); |
180 | 0 | } |
181 | 2 | } |
182 | 2 | return false; |
183 | 2 | } |
184 | | |
185 | | private: |
186 | 3.08k | bool is_named_wildcard(const std::string& key) { |
187 | 3.08k | if (key.find('{') != std::string::npos && key.find('}') != std::string::npos) { |
188 | 352 | return true; |
189 | 352 | } |
190 | 2.73k | return false; |
191 | 3.08k | } |
192 | | |
193 | 176 | std::string extract_template(const std::string& key) { |
194 | 176 | std::size_t left = key.find_first_of('{') + 1; |
195 | 176 | std::size_t right = key.find_last_of('}'); |
196 | 176 | return key.substr(left, right - left); |
197 | 176 | } |
198 | | |
199 | 26.1k | TrieNode* get_child(const std::string& key) { |
200 | 26.1k | auto pair = _children.find(key); |
201 | 26.1k | if (pair == _children.end()) { |
202 | 5.27k | return nullptr; |
203 | 5.27k | } |
204 | 20.8k | return pair->second; |
205 | 26.1k | } |
206 | | |
207 | | void put(std::map<std::string, std::string>* params, TrieNode* node, |
208 | 20.2k | const std::string& token) { |
209 | 20.2k | if (params != nullptr && !node->_named_wildcard.empty()) { |
210 | 4.34k | params->insert(std::make_pair(node->_named_wildcard, token)); |
211 | 4.34k | } |
212 | 20.2k | } |
213 | | |
214 | | T* _value; |
215 | | std::string _wildcard; |
216 | | std::string _named_wildcard; |
217 | | std::map<std::string, TrieNode*> _children; |
218 | | Allocator _allocator; |
219 | | }; |
220 | | |
221 | 641 | bool insert(const std::string& path, const T& value) { |
222 | 641 | std::vector<std::string> path_array; |
223 | 641 | split(path, &path_array); |
224 | 641 | if (path_array.empty()) { |
225 | 8 | if (_root_value == nullptr) { |
226 | 8 | _root_value = _allocator.allocate(1); |
227 | 8 | _allocator.construct(_root_value, value); |
228 | 8 | return true; |
229 | 8 | } else { |
230 | 0 | return false; |
231 | 0 | } |
232 | 8 | } |
233 | 633 | int index = 0; |
234 | 633 | if (path_array[0].empty()) { |
235 | 0 | index = 1; |
236 | 0 | } |
237 | 633 | return _root.insert(path_array, index, value); |
238 | 641 | } |
239 | | |
240 | | bool retrieve(const std::string& path, T* value) { return retrieve(path, value, nullptr); } |
241 | | |
242 | 6.13k | bool retrieve(const std::string& path, T* value, std::map<std::string, std::string>* params) { |
243 | 6.13k | if (path.empty()) { |
244 | 0 | if (_root_value == nullptr) { |
245 | 0 | return false; |
246 | 0 | } else { |
247 | 0 | _allocator.construct(value, *_root_value); |
248 | 0 | return true; |
249 | 0 | } |
250 | 0 | } |
251 | 6.13k | std::vector<std::string> path_array; |
252 | 6.13k | split(path, &path_array); |
253 | 6.13k | if (path_array.empty()) { |
254 | 0 | if (_root_value == nullptr) { |
255 | 0 | return false; |
256 | 0 | } else { |
257 | 0 | _allocator.construct(value, *_root_value); |
258 | 0 | return true; |
259 | 0 | } |
260 | 0 | } |
261 | 6.13k | int index = 0; |
262 | 6.13k | if (path_array[0].empty()) { |
263 | 0 | index = 1; |
264 | 0 | } |
265 | 6.13k | return _root.retrieve(path_array, index, value, params); |
266 | 6.13k | } |
267 | | |
268 | | private: |
269 | 6.77k | void split(const std::string& path, std::vector<std::string>* array) { |
270 | 6.77k | const char* path_str = path.c_str(); |
271 | 6.77k | std::size_t start = 0; |
272 | 6.77k | std::size_t pos = 0; |
273 | 286k | for (; pos < path.length(); ++pos) { |
274 | 280k | if (path_str[pos] == _separator) { |
275 | 21.7k | if (pos - start > 0) { |
276 | 14.9k | array->push_back(path.substr(start, pos - start)); |
277 | 14.9k | } |
278 | 21.7k | start = pos + 1; |
279 | 21.7k | } |
280 | 280k | } |
281 | 6.77k | if (pos - start > 0) { |
282 | 6.76k | array->push_back(path.substr(start, pos - start)); |
283 | 6.76k | } |
284 | 6.77k | } |
285 | | |
286 | | TrieNode _root; |
287 | | T* _root_value; |
288 | | char _separator; |
289 | | Allocator _allocator; |
290 | | }; |
291 | | |
292 | | } // namespace doris |