# Proposed changes This PR fixed lots of issues when building from source on macOS with Apple M1 chip. ## ATTENTION The job for supporting macOS with Apple M1 chip is too big and there are lots of unresolved issues during runtime: 1. Some errors with memory tracker occur when BE (RELEASE) starts. 2. Some UT cases fail. ... Temporarily, the following changes are made on macOS to start BE successfully. 1. Disable memory tracker. 2. Use tcmalloc instead of jemalloc. This PR kicks off the job. Guys who are interested in this job can continue to fix these runtime issues. ## Use case ```shell ./build.sh -j 8 --be --clean cd output/be/bin ulimit -n 60000 ./start_be.sh --daemon ``` ## Something else It takes around _**10+**_ minutes to build BE (with prebuilt third-parties) on macOS with M1 chip. We will improve the development experience on macOS greatly when we finish the adaptation job.
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
|