Skip to content

Commit 9948424

Browse files
committed
optimize trie
1 parent c15595c commit 9948424

4 files changed

Lines changed: 99 additions & 44 deletions

File tree

.gitignore

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ compile_commands.json
1818
echo_server
1919

2020
# IDE/Editor
21-
.cache/
22-
.vscode/
23-
.idea/
24-
.agents/
25-
.codex/
21+
*.cache/
22+
*.vscode/
23+
*.idea/
24+
*.agents/
25+
*.codex/
2626
*.swp
2727
*.swo
2828
*~

server/include/trie.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,38 @@
33
#include <functional>
44
#include <memory>
55
#include <optional>
6+
#include <string>
7+
#include <string_view>
68
#include <unordered_map>
9+
#include <vector>
710
namespace HTTP {
811
using RespondType = std::function<ResponseData(const RequestData &)>;
912
class Trie {
13+
struct StringHash {
14+
using is_transparent = void;
15+
size_t operator()(std::string_view value) const noexcept;
16+
};
17+
struct StringEqual {
18+
using is_transparent = void;
19+
bool operator()(std::string_view lhs, std::string_view rhs) const noexcept;
20+
};
1021
struct Node {
11-
std::unordered_map<char, std::unique_ptr<Node>> children;
22+
std::unordered_map<std::string, std::unique_ptr<Node>, StringHash,
23+
StringEqual>
24+
children;
25+
std::unique_ptr<Node> wildcard;
1226
std::optional<RespondType> handlers[5];
1327
Node() = default;
14-
bool any = false;
15-
Node &Move(char c);
16-
const Node &Move(char c) const;
28+
Node &Move(std::string_view segment);
1729
};
1830
std::unique_ptr<Node> root_ = std::make_unique<Node>();
1931

2032
public:
2133
Trie() = default;
2234
Trie(Trie &&rhs);
2335
Trie &operator=(Trie &&rhs);
24-
const Node &GetRoot();
2536
void AddRequest(Method type, RespondType function, std::string_view path);
37+
RespondType Match(Method method, std::string_view path,
38+
std::vector<std::string> &urlVariables) const;
2639
};
2740
} // namespace HTTP

server/src/server.cpp

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,8 @@ CoFuture<void> Server::GetHandler(RequestData &data, ReadIterator &iter, Respond
259259
if (*iter != '/') {
260260
throw HTTPError(400, "Invalid request");
261261
}
262-
auto current = &trie_.GetRoot();
263-
bool inVariable = false;
262+
std::string path;
263+
path.reserve(64);
264264
while (true) {
265265
co_await iter.Ensure();
266266
if (!iter) {
@@ -270,23 +270,10 @@ CoFuture<void> Server::GetHandler(RequestData &data, ReadIterator &iter, Respond
270270
if (c == ' ' || c == '?') {
271271
break;
272272
}
273-
if (!current->children.contains(c) && current->any) {
274-
if (!inVariable) {
275-
inVariable = true;
276-
data.urlVariables.push_back("");
277-
}
278-
data.urlVariables.back().push_back(c);
279-
co_await ++iter;
280-
continue;
281-
}
282-
inVariable = false;
283-
current = &current->Move(c);
273+
path.push_back(c);
284274
co_await ++iter;
285275
}
286-
if (!current->handlers[data.method]) {
287-
throw HTTPError(404, "Not found");
288-
}
289-
handler = *current->handlers[data.method];
276+
handler = trie_.Match(data.method, path, data.urlVariables);
290277
co_return;
291278
}
292279

server/src/trie.cpp

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,87 @@
22
#include "http_error.h"
33
#include "request_data.h"
44
namespace HTTP {
5-
Trie::Node &Trie::Node::Move(char c) {
6-
if (c == '*') {
7-
any = true;
8-
return *this;
9-
}
10-
if (!children.contains(c)) {
11-
children[c] = std::make_unique<Node>();
12-
}
13-
return *children[c];
5+
6+
size_t Trie::StringHash::operator()(std::string_view value) const noexcept {
7+
return std::hash<std::string_view>{}(value);
148
}
15-
const Trie::Node &Trie::Node::Move(char c) const {
16-
try {
17-
return *children.at(c);
18-
} catch (...) {
19-
throw HTTPError(404, "Not found");
9+
10+
bool Trie::StringEqual::operator()(std::string_view lhs,
11+
std::string_view rhs) const noexcept {
12+
return lhs == rhs;
13+
}
14+
15+
Trie::Node &Trie::Node::Move(std::string_view segment) {
16+
if (segment == "*") {
17+
if (!wildcard) {
18+
wildcard = std::make_unique<Node>();
19+
}
20+
return *wildcard;
21+
}
22+
23+
auto child = children.find(segment);
24+
if (child == children.end()) {
25+
auto [inserted, _] =
26+
children.emplace(std::string{segment}, std::make_unique<Node>());
27+
child = inserted;
2028
}
29+
return *child->second;
2130
}
31+
2232
void Trie::AddRequest(Method method, RespondType respond,
2333
std::string_view path) {
2434
Node *current = root_.get();
25-
for (auto c : path) {
26-
current = &current->Move(c);
35+
size_t position = path.starts_with('/') ? 1 : 0;
36+
while (position < path.size()) {
37+
const size_t next = path.find('/', position);
38+
const auto segment =
39+
next == std::string_view::npos
40+
? path.substr(position)
41+
: path.substr(position, next - position);
42+
current = &current->Move(segment);
43+
if (next == std::string_view::npos) {
44+
break;
45+
}
46+
position = next + 1;
2747
}
2848
current->handlers[method] = respond;
2949
}
30-
const Trie::Node &Trie::GetRoot() { return *root_; }
50+
51+
RespondType Trie::Match(Method method, std::string_view path,
52+
std::vector<std::string> &urlVariables) const {
53+
const Node *current = root_.get();
54+
urlVariables.clear();
55+
56+
size_t position = path.starts_with('/') ? 1 : 0;
57+
while (position < path.size()) {
58+
const size_t next = path.find('/', position);
59+
const auto segment =
60+
next == std::string_view::npos
61+
? path.substr(position)
62+
: path.substr(position, next - position);
63+
64+
const auto child = current->children.find(segment);
65+
if (child != current->children.end()) {
66+
current = child->second.get();
67+
} else if (current->wildcard) {
68+
urlVariables.emplace_back(segment);
69+
current = current->wildcard.get();
70+
} else {
71+
throw HTTPError(404, "Not found");
72+
}
73+
74+
if (next == std::string_view::npos) {
75+
break;
76+
}
77+
position = next + 1;
78+
}
79+
80+
if (!current->handlers[method]) {
81+
throw HTTPError(404, "Not found");
82+
}
83+
return *current->handlers[method];
84+
}
85+
3186
Trie::Trie(Trie &&rhs) { root_ = std::move(rhs.root_); }
3287
Trie &Trie::operator=(Trie &&rhs) {
3388
root_ = std::move(rhs.root_);

0 commit comments

Comments
 (0)