include/ylt/standalone/cinatra/coro_radix_tree.hpp (329 lines of code) (raw):
#pragma once
#include <async_simple/coro/Lazy.h>
#include <functional>
#include <string>
#include <string_view>
#include <tuple>
#include <vector>
#include "cinatra/coro_http_request.hpp"
#include "coro_http_response.hpp"
#include "ylt/util/type_traits.h"
namespace cinatra {
constexpr char type_asterisk = '*';
constexpr char type_colon = ':';
constexpr char type_slash = '/';
typedef std::tuple<
bool, std::function<void(coro_http_request &req, coro_http_response &resp)>,
std::unordered_map<std::string, std::string>>
parse_result;
typedef std::tuple<bool,
std::function<async_simple::coro::Lazy<void>(
coro_http_request &req, coro_http_response &resp)>,
std::unordered_map<std::string, std::string>>
coro_result;
struct handler_t {
std::string method;
std::function<void(coro_http_request &req, coro_http_response &resp)> handler;
};
struct coro_handler_t {
std::string method;
std::function<async_simple::coro::Lazy<void>(coro_http_request &req,
coro_http_response &resp)>
coro_handler;
};
struct radix_tree_node {
std::string path;
handler_t handler;
coro_handler_t coro_handler;
std::string indices;
std::vector<std::shared_ptr<radix_tree_node>> children;
int max_params;
radix_tree_node() = default;
radix_tree_node(const std::string &path) { this->path = path; }
~radix_tree_node() {}
std::function<void(coro_http_request &req, coro_http_response &resp)>
get_handler(const std::string &method) {
if (handler.method == method) {
return handler.handler;
}
return nullptr;
}
std::function<async_simple::coro::Lazy<void>(coro_http_request &req,
coro_http_response &resp)>
get_coro_handler(const std::string &method) {
if (coro_handler.method == method) {
return coro_handler.coro_handler;
}
return nullptr;
}
int add_handler(
std::function<void(coro_http_request &req, coro_http_response &resp)>
handler,
const std::string &method) {
this->handler = handler_t{method, handler};
return 0;
}
int add_coro_handler(std::function<async_simple::coro::Lazy<void>(
coro_http_request &req, coro_http_response &resp)>
coro_handler,
const std::string &method) {
this->coro_handler = coro_handler_t{method, coro_handler};
return 0;
}
std::shared_ptr<radix_tree_node> insert_child(
char index, std::shared_ptr<radix_tree_node> child) {
auto i = this->get_index_position(index);
this->indices.insert(this->indices.begin() + i, index);
this->children.insert(this->children.begin() + i, child);
return child;
}
std::shared_ptr<radix_tree_node> get_child(char index) {
auto i = this->get_index_position(index);
return this->indices[i] != index ? nullptr : this->children[i];
}
int get_index_position(char target) {
int low = 0, high = this->indices.size(), mid;
while (low < high) {
mid = low + ((high - low) >> 1);
if (this->indices[mid] < target)
low = mid + 1;
else
high = mid;
}
return low;
}
};
class radix_tree {
public:
radix_tree() {
this->root = std::make_shared<radix_tree_node>(radix_tree_node());
}
~radix_tree() {}
int insert(
const std::string &path,
std::function<void(coro_http_request &req, coro_http_response &resp)>
handler,
const std::string &method) {
auto root = this->root;
int i = 0, n = path.size(), param_count = 0, code = 0;
while (i < n) {
if (!root->indices.empty() &&
(root->indices[0] == type_asterisk || path[i] == type_asterisk ||
(path[i] != type_colon && root->indices[0] == type_colon) ||
(path[i] == type_colon && root->indices[0] != type_colon) ||
(path[i] == type_colon && root->indices[0] == type_colon &&
path.substr(i + 1, find_pos(path, type_slash, i) - i - 1) !=
root->children[0]->path))) {
code = -1;
break;
}
auto child = root->get_child(path[i]);
if (!child) {
auto p = find_pos(path, type_colon, i);
if (p == n) {
p = find_pos(path, type_asterisk, i);
root = root->insert_child(path[i], std::make_shared<radix_tree_node>(
path.substr(i, p - i)));
if (p < n) {
root = root->insert_child(
type_asterisk,
std::make_shared<radix_tree_node>(path.substr(p + 1)));
++param_count;
}
code = root->add_handler(handler, method);
break;
}
root = root->insert_child(
path[i], std::make_shared<radix_tree_node>(path.substr(i, p - i)));
i = find_pos(path, type_slash, p);
root = root->insert_child(
type_colon,
std::make_shared<radix_tree_node>(path.substr(p + 1, i - p - 1)));
++param_count;
if (i == n) {
code = root->add_handler(handler, method);
break;
}
}
else {
root = child;
if (path[i] == type_colon) {
++param_count;
i += root->path.size() + 1;
if (i == n) {
code = root->add_handler(handler, method);
break;
}
}
else {
auto j = 0UL;
auto m = root->path.size();
for (; i < n && j < m && path[i] == root->path[j]; ++i, ++j) {
}
if (j < m) {
std::shared_ptr<radix_tree_node> child(
std::make_shared<radix_tree_node>(root->path.substr(j)));
child->handler = root->handler;
child->indices = root->indices;
child->children = root->children;
root->path = root->path.substr(0, j);
root->handler = {};
root->indices = child->path[0];
root->children = {child};
}
if (i == n) {
code = root->add_handler(handler, method);
break;
}
}
}
}
if (param_count > this->root->max_params)
this->root->max_params = param_count;
return code;
}
int coro_insert(const std::string &path,
std::function<async_simple::coro::Lazy<void>(
coro_http_request &req, coro_http_response &resp)>
coro_handler,
std::string &method) {
auto root = this->root;
int i = 0, n = path.size(), param_count = 0, code = 0;
while (i < n) {
if (!root->indices.empty() &&
(root->indices[0] == type_asterisk || path[i] == type_asterisk ||
(path[i] != type_colon && root->indices[0] == type_colon) ||
(path[i] == type_colon && root->indices[0] != type_colon) ||
(path[i] == type_colon && root->indices[0] == type_colon &&
path.substr(i + 1, find_pos(path, type_slash, i) - i - 1) !=
root->children[0]->path))) {
code = -1;
break;
}
auto child = root->get_child(path[i]);
if (!child) {
auto p = find_pos(path, type_colon, i);
if (p == n) {
p = find_pos(path, type_asterisk, i);
root = root->insert_child(path[i], std::make_shared<radix_tree_node>(
path.substr(i, p - i)));
if (p < n) {
root = root->insert_child(
type_asterisk,
std::make_shared<radix_tree_node>(path.substr(p + 1)));
++param_count;
}
code = root->add_coro_handler(coro_handler, method);
break;
}
root = root->insert_child(
path[i], std::make_shared<radix_tree_node>(path.substr(i, p - i)));
i = find_pos(path, type_slash, p);
root = root->insert_child(
type_colon,
std::make_shared<radix_tree_node>(path.substr(p + 1, i - p - 1)));
++param_count;
if (i == n) {
code = root->add_coro_handler(coro_handler, method);
break;
}
}
else {
root = child;
if (path[i] == type_colon) {
++param_count;
i += root->path.size() + 1;
if (i == n) {
code = root->add_coro_handler(coro_handler, method);
break;
}
}
else {
auto j = 0UL;
auto m = root->path.size();
for (; i < n && j < m && path[i] == root->path[j]; ++i, ++j) {
}
if (j < m) {
std::shared_ptr<radix_tree_node> child(
std::make_shared<radix_tree_node>(root->path.substr(j)));
child->coro_handler = root->coro_handler;
child->indices = root->indices;
child->children = root->children;
root->path = root->path.substr(0, j);
root->coro_handler = {};
root->indices = child->path[0];
root->children = {child};
}
if (i == n) {
code = root->add_coro_handler(coro_handler, method);
break;
}
}
}
}
if (param_count > this->root->max_params)
this->root->max_params = param_count;
return code;
}
parse_result get(const std::string &path, const std::string &method) {
std::unordered_map<std::string, std::string> params;
auto root = this->root;
int i = 0, n = path.size(), p;
while (i < n) {
if (root->indices.empty())
return parse_result();
if (root->indices[0] == type_colon) {
root = root->children[0];
p = find_pos(path, type_slash, i);
params[root->path] = path.substr(i, p - i);
i = p;
}
else if (root->indices[0] == type_asterisk) {
root = root->children[0];
params[root->path] = path.substr(i);
break;
}
else {
root = root->get_child(path[i]);
if (!root || path.substr(i, root->path.size()) != root->path)
return parse_result();
i += root->path.size();
}
}
return parse_result{true, root->get_handler(method), params};
}
coro_result get_coro(const std::string &path, const std::string &method) {
std::unordered_map<std::string, std::string> params;
auto root = this->root;
int i = 0, n = path.size(), p;
while (i < n) {
if (root->indices.empty())
return coro_result();
if (root->indices[0] == type_colon) {
root = root->children[0];
p = find_pos(path, type_slash, i);
params[root->path] = path.substr(i, p - i);
i = p;
}
else if (root->indices[0] == type_asterisk) {
root = root->children[0];
params[root->path] = path.substr(i);
break;
}
else {
root = root->get_child(path[i]);
if (!root || path.substr(i, root->path.size()) != root->path)
return coro_result();
i += root->path.size();
}
}
return coro_result{true, root->get_coro_handler(method), params};
}
private:
int find_pos(const std::string &str, char target, int start) {
auto i = str.find(target, start);
return i == -1 ? str.size() : i;
}
std::shared_ptr<radix_tree_node> root;
};
} // namespace cinatra