Coverage Report

Created: 2026-03-19 11:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
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
78
    PathTrie() : _root("/", "*"), _root_value(nullptr), _separator('/') {}
32
33
54
    ~PathTrie() {
34
54
        if (_root_value != nullptr) {
35
5
            _allocator.destroy(_root_value);
36
5
            _allocator.deallocate(_root_value, 1);
37
5
        }
38
54
    }
39
40
    class Allocator {
41
    public:
42
        using value_type = T;
43
44
706
        T* allocate(size_t n) { return static_cast<T*>(::operator new(sizeof(T) * n)); }
45
46
        template <typename... Args>
47
7.04k
        void construct(T* p, Args&&... args) {
48
7.04k
            new (p) T(std::forward<Args>(args)...);
49
7.04k
        }
_ZN5doris8PathTrieIPNS_11HttpHandlerEE9Allocator9constructIJRKS2_EEEvPS2_DpOT_
Line
Count
Source
47
706
        void construct(T* p, Args&&... args) {
48
706
            new (p) T(std::forward<Args>(args)...);
49
706
        }
_ZN5doris8PathTrieIPNS_11HttpHandlerEE9Allocator9constructIJRS2_EEEvPS2_DpOT_
Line
Count
Source
47
6.33k
        void construct(T* p, Args&&... args) {
48
6.33k
            new (p) T(std::forward<Args>(args)...);
49
6.33k
        }
50
51
385
        void destroy(T* p) { p->~T(); }
52
53
385
        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
400
                : _value(nullptr), _wildcard(wildcard) {
60
400
            if (is_named_wildcard(key)) {
61
45
                _named_wildcard = extract_template(key);
62
45
            }
63
400
        }
64
65
        TrieNode(const std::string& key, const T& value, const std::string& wildcard)
66
697
                : _value(nullptr), _wildcard(wildcard) {
67
697
            _value = _allocator.allocate(1);
68
697
            _allocator.construct(_value, value);
69
697
            if (is_named_wildcard(key)) {
70
90
                _named_wildcard = extract_template(key);
71
90
            }
72
697
        }
73
74
618
        ~TrieNode() {
75
618
            for (auto& iter : _children) {
76
564
                delete iter.second;
77
564
                iter.second = nullptr;
78
564
            }
79
618
            if (_value != nullptr) {
80
380
                _allocator.destroy(_value);
81
380
                _allocator.deallocate(_value, 1);
82
380
            }
83
618
        }
84
85
        // Return true if insert success.
86
1.66k
        bool insert(const std::vector<std::string> path, int index, const T& value) {
87
1.66k
            if (index >= path.size()) {
88
0
                return false;
89
0
            }
90
1.66k
            const std::string& token = path[index];
91
1.66k
            std::string key = token;
92
93
1.66k
            if (is_named_wildcard(token)) {
94
198
                key = _wildcard;
95
198
            }
96
97
1.66k
            TrieNode* node = get_child(key);
98
99
1.66k
            if (node == nullptr) {
100
                // no exist child for this key
101
1.01k
                if (index == path.size() - 1) {
102
697
                    node = new TrieNode(token, value, _wildcard);
103
697
                    _children.insert(std::make_pair(key, node));
104
697
                    return true;
105
697
                } else {
106
322
                    node = new TrieNode(token, _wildcard);
107
322
                    _children.insert(std::make_pair(key, node));
108
322
                }
109
1.01k
            } else {
110
                // If this is a template, set this to the node
111
642
                if (is_named_wildcard(token)) {
112
63
                    std::string temp = extract_template(token);
113
63
                    if (node->_named_wildcard.empty() || node->_named_wildcard.compare(temp) == 0) {
114
63
                        node->_named_wildcard = temp;
115
63
                    } else {
116
                        // Duplicated
117
0
                        return false;
118
0
                    }
119
63
                }
120
642
                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
642
            }
130
964
            return node->insert(path, index + 1, value);
131
1.66k
        }
132
133
        bool retrieve(const std::vector<std::string> path, int index, T* value,
134
20.8k
                      std::map<std::string, std::string>* params) {
135
            // check max index
136
20.8k
            if (index >= path.size()) {
137
0
                return false;
138
0
            }
139
20.8k
            bool use_wildcard = false;
140
20.8k
            const std::string& token = path[index];
141
20.8k
            TrieNode* node = get_child(token);
142
20.8k
            if (node == nullptr) {
143
4.37k
                node = get_child(_wildcard);
144
4.37k
                if (node == nullptr) {
145
4
                    return false;
146
4
                }
147
4.37k
                use_wildcard = true;
148
16.4k
            } else {
149
                // If we the last one, but we have no value, check wildcard
150
16.4k
                if (index == path.size() - 1 && node->_value == nullptr &&
151
16.4k
                    get_child(_wildcard) != nullptr) {
152
0
                    node = get_child(_wildcard);
153
0
                    use_wildcard = true;
154
16.4k
                } else {
155
16.4k
                    use_wildcard = (token.compare(_wildcard) == 0);
156
16.4k
                }
157
16.4k
            }
158
159
20.8k
            put(params, node, token);
160
161
20.8k
            if (index == path.size() - 1) {
162
6.33k
                if (node->_value == nullptr) {
163
0
                    return false;
164
0
                }
165
6.33k
                _allocator.construct(value, *node->_value);
166
6.33k
                return true;
167
6.33k
            }
168
169
            // find exact
170
14.4k
            if (node->retrieve(path, index + 1, value, params)) {
171
14.4k
                return true;
172
14.4k
            }
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.40k
        bool is_named_wildcard(const std::string& key) {
187
3.40k
            if (key.find('{') != std::string::npos && key.find('}') != std::string::npos) {
188
396
                return true;
189
396
            }
190
3.00k
            return false;
191
3.40k
        }
192
193
198
        std::string extract_template(const std::string& key) {
194
198
            std::size_t left = key.find_first_of('{') + 1;
195
198
            std::size_t right = key.find_last_of('}');
196
198
            return key.substr(left, right - left);
197
198
        }
198
199
26.8k
        TrieNode* get_child(const std::string& key) {
200
26.8k
            auto pair = _children.find(key);
201
26.8k
            if (pair == _children.end()) {
202
5.40k
                return nullptr;
203
5.40k
            }
204
21.4k
            return pair->second;
205
26.8k
        }
206
207
        void put(std::map<std::string, std::string>* params, TrieNode* node,
208
20.8k
                 const std::string& token) {
209
20.8k
            if (params != nullptr && !node->_named_wildcard.empty()) {
210
4.37k
                params->insert(std::make_pair(node->_named_wildcard, token));
211
4.37k
            }
212
20.8k
        }
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
706
    bool insert(const std::string& path, const T& value) {
222
706
        std::vector<std::string> path_array;
223
706
        split(path, &path_array);
224
706
        if (path_array.empty()) {
225
9
            if (_root_value == nullptr) {
226
9
                _root_value = _allocator.allocate(1);
227
9
                _allocator.construct(_root_value, value);
228
9
                return true;
229
9
            } else {
230
0
                return false;
231
0
            }
232
9
        }
233
697
        int index = 0;
234
697
        if (path_array[0].empty()) {
235
0
            index = 1;
236
0
        }
237
697
        return _root.insert(path_array, index, value);
238
706
    }
239
240
    bool retrieve(const std::string& path, T* value) { return retrieve(path, value, nullptr); }
241
242
6.33k
    bool retrieve(const std::string& path, T* value, std::map<std::string, std::string>* params) {
243
6.33k
        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.33k
        std::vector<std::string> path_array;
252
6.33k
        split(path, &path_array);
253
6.33k
        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.33k
        int index = 0;
262
6.33k
        if (path_array[0].empty()) {
263
0
            index = 1;
264
0
        }
265
6.33k
        return _root.retrieve(path_array, index, value, params);
266
6.33k
    }
267
268
private:
269
7.04k
    void split(const std::string& path, std::vector<std::string>* array) {
270
7.04k
        const char* path_str = path.c_str();
271
7.04k
        std::size_t start = 0;
272
7.04k
        std::size_t pos = 0;
273
293k
        for (; pos < path.length(); ++pos) {
274
286k
            if (path_str[pos] == _separator) {
275
22.4k
                if (pos - start > 0) {
276
15.4k
                    array->push_back(path.substr(start, pos - start));
277
15.4k
                }
278
22.4k
                start = pos + 1;
279
22.4k
            }
280
286k
        }
281
7.04k
        if (pos - start > 0) {
282
7.03k
            array->push_back(path.substr(start, pos - start));
283
7.03k
        }
284
7.04k
    }
285
286
    TrieNode _root;
287
    T* _root_value;
288
    char _separator;
289
    Allocator _allocator;
290
};
291
292
} // namespace doris