293 lines
9.5 KiB
C++
293 lines
9.5 KiB
C++
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
#pragma once
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace doris {
|
|
|
|
// This tree is usd for manage restful api path.
|
|
template <class T>
|
|
class PathTrie {
|
|
public:
|
|
PathTrie() : _root("/", "*"), _root_value(nullptr), _separator('/') {}
|
|
|
|
~PathTrie() {
|
|
if (_root_value != nullptr) {
|
|
_allocator.destroy(_root_value);
|
|
_allocator.deallocate(_root_value, 1);
|
|
}
|
|
}
|
|
|
|
class Allocator {
|
|
public:
|
|
using value_type = T;
|
|
|
|
T* allocate(size_t n) { return static_cast<T*>(::operator new(sizeof(T) * n)); }
|
|
|
|
template <typename... Args>
|
|
void construct(T* p, Args&&... args) {
|
|
new (p) T(std::forward<Args>(args)...);
|
|
}
|
|
|
|
void destroy(T* p) { p->~T(); }
|
|
|
|
void deallocate(T* p, size_t n) { ::operator delete(p); }
|
|
};
|
|
|
|
class TrieNode {
|
|
public:
|
|
TrieNode(const std::string& key, const std::string& wildcard)
|
|
: _value(nullptr), _wildcard(wildcard) {
|
|
if (is_named_wildcard(key)) {
|
|
_named_wildcard = extract_template(key);
|
|
}
|
|
}
|
|
|
|
TrieNode(const std::string& key, const T& value, const std::string& wildcard)
|
|
: _value(nullptr), _wildcard(wildcard) {
|
|
_value = _allocator.allocate(1);
|
|
_allocator.construct(_value, value);
|
|
if (is_named_wildcard(key)) {
|
|
_named_wildcard = extract_template(key);
|
|
}
|
|
}
|
|
|
|
~TrieNode() {
|
|
for (auto& iter : _children) {
|
|
delete iter.second;
|
|
iter.second = nullptr;
|
|
}
|
|
if (_value != nullptr) {
|
|
_allocator.destroy(_value);
|
|
_allocator.deallocate(_value, 1);
|
|
}
|
|
}
|
|
|
|
// Return true if insert success.
|
|
bool insert(const std::vector<std::string> path, int index, const T& value) {
|
|
if (index >= path.size()) {
|
|
return false;
|
|
}
|
|
const std::string& token = path[index];
|
|
std::string key = token;
|
|
|
|
if (is_named_wildcard(token)) {
|
|
key = _wildcard;
|
|
}
|
|
|
|
TrieNode* node = get_child(key);
|
|
|
|
if (node == nullptr) {
|
|
// no exist child for this key
|
|
if (index == path.size() - 1) {
|
|
node = new TrieNode(token, value, _wildcard);
|
|
_children.insert(std::make_pair(key, node));
|
|
return true;
|
|
} else {
|
|
node = new TrieNode(token, _wildcard);
|
|
_children.insert(std::make_pair(key, node));
|
|
}
|
|
} else {
|
|
// If this is a template, set this to the node
|
|
if (is_named_wildcard(token)) {
|
|
std::string temp = extract_template(token);
|
|
if (node->_named_wildcard.empty() || node->_named_wildcard.compare(temp) == 0) {
|
|
node->_named_wildcard = temp;
|
|
} else {
|
|
// Duplicated
|
|
return false;
|
|
}
|
|
}
|
|
if (index == path.size() - 1) {
|
|
if (node->_value == nullptr) {
|
|
node->_value = _allocator.allocate(1);
|
|
_allocator.construct(node->_value, value);
|
|
return true;
|
|
}
|
|
// Already register by other path
|
|
return false;
|
|
}
|
|
}
|
|
return node->insert(path, index + 1, value);
|
|
}
|
|
|
|
bool retrieve(const std::vector<std::string> path, int index, T* value,
|
|
std::map<std::string, std::string>* params) {
|
|
// check max index
|
|
if (index >= path.size()) {
|
|
return false;
|
|
}
|
|
bool use_wildcard = false;
|
|
const std::string& token = path[index];
|
|
TrieNode* node = get_child(token);
|
|
if (node == nullptr) {
|
|
node = get_child(_wildcard);
|
|
if (node == nullptr) {
|
|
return false;
|
|
}
|
|
use_wildcard = true;
|
|
} else {
|
|
// If we the last one, but we have no value, check wildcard
|
|
if (index == path.size() - 1 && node->_value == nullptr &&
|
|
get_child(_wildcard) != nullptr) {
|
|
node = get_child(_wildcard);
|
|
use_wildcard = true;
|
|
} else {
|
|
use_wildcard = (token.compare(_wildcard) == 0);
|
|
}
|
|
}
|
|
|
|
put(params, node, token);
|
|
|
|
if (index == path.size() - 1) {
|
|
if (node->_value == nullptr) {
|
|
return false;
|
|
}
|
|
_allocator.construct(value, *node->_value);
|
|
return true;
|
|
}
|
|
|
|
// find exact
|
|
if (node->retrieve(path, index + 1, value, params)) {
|
|
return true;
|
|
}
|
|
|
|
// backtrace to test if wildcard can match
|
|
if (!use_wildcard) {
|
|
node = get_child(_wildcard);
|
|
if (node != nullptr) {
|
|
put(params, node, token);
|
|
return node->retrieve(path, index + 1, value, params);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
bool is_named_wildcard(const std::string& key) {
|
|
if (key.find('{') != std::string::npos && key.find('}') != std::string::npos) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string extract_template(const std::string& key) {
|
|
std::size_t left = key.find_first_of('{') + 1;
|
|
std::size_t right = key.find_last_of('}');
|
|
return key.substr(left, right - left);
|
|
}
|
|
|
|
TrieNode* get_child(const std::string& key) {
|
|
auto pair = _children.find(key);
|
|
if (pair == _children.end()) {
|
|
return nullptr;
|
|
}
|
|
return pair->second;
|
|
}
|
|
|
|
void put(std::map<std::string, std::string>* params, TrieNode* node,
|
|
const std::string& token) {
|
|
if (params != nullptr && !node->_named_wildcard.empty()) {
|
|
params->insert(std::make_pair(node->_named_wildcard, token));
|
|
}
|
|
}
|
|
|
|
T* _value;
|
|
std::string _wildcard;
|
|
std::string _named_wildcard;
|
|
std::map<std::string, TrieNode*> _children;
|
|
Allocator _allocator;
|
|
};
|
|
|
|
bool insert(const std::string& path, const T& value) {
|
|
std::vector<std::string> path_array;
|
|
split(path, &path_array);
|
|
if (path_array.empty()) {
|
|
if (_root_value == nullptr) {
|
|
_root_value = _allocator.allocate(1);
|
|
_allocator.construct(_root_value, value);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
int index = 0;
|
|
if (path_array[0].empty()) {
|
|
index = 1;
|
|
}
|
|
return _root.insert(path_array, index, value);
|
|
}
|
|
|
|
bool retrieve(const std::string& path, T* value) { return retrieve(path, value, nullptr); }
|
|
|
|
bool retrieve(const std::string& path, T* value, std::map<std::string, std::string>* params) {
|
|
if (path.empty()) {
|
|
if (_root_value == nullptr) {
|
|
return false;
|
|
} else {
|
|
_allocator.construct(value, *_root_value);
|
|
return true;
|
|
}
|
|
}
|
|
std::vector<std::string> path_array;
|
|
split(path, &path_array);
|
|
if (path_array.empty()) {
|
|
if (_root_value == nullptr) {
|
|
return false;
|
|
} else {
|
|
_allocator.construct(value, *_root_value);
|
|
return true;
|
|
}
|
|
}
|
|
int index = 0;
|
|
if (path_array[0].empty()) {
|
|
index = 1;
|
|
}
|
|
return _root.retrieve(path_array, index, value, params);
|
|
}
|
|
|
|
private:
|
|
void split(const std::string& path, std::vector<std::string>* array) {
|
|
const char* path_str = path.c_str();
|
|
std::size_t start = 0;
|
|
std::size_t pos = 0;
|
|
for (; pos < path.length(); ++pos) {
|
|
if (path_str[pos] == _separator) {
|
|
if (pos - start > 0) {
|
|
array->push_back(path.substr(start, pos - start));
|
|
}
|
|
start = pos + 1;
|
|
}
|
|
}
|
|
if (pos - start > 0) {
|
|
array->push_back(path.substr(start, pos - start));
|
|
}
|
|
}
|
|
|
|
TrieNode _root;
|
|
T* _root_value;
|
|
char _separator;
|
|
Allocator _allocator;
|
|
};
|
|
|
|
} // namespace doris
|