feat add exposed_var

This commit is contained in:
tqcq 2024-07-03 18:30:06 +08:00
parent 2b69c377f7
commit efbb78f652
7 changed files with 456 additions and 24776 deletions

View File

@ -52,8 +52,8 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load,")
# set(NO_WHOLE_ARCHIVE_PREFIX "")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gz")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz")
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
# set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
@ -153,6 +153,7 @@ set(TILE_SRCS
"tile/base/encoding/detail/hex_chars.cc"
"tile/base/encoding/hex.cc"
"tile/base/encoding/percent.cc"
"tile/base/exposed_var.cc"
"tile/base/internal/background_task_host.cc"
"tile/base/internal/background_task_host.h"
"tile/base/internal/case_insensitive_hash_map.h"
@ -279,6 +280,7 @@ if(TILE_BUILD_TESTS)
target_sources(${PROJECT_NAME}_test_all PRIVATE ${test_file})
endmacro()
tile_add_test(base_exposed_var_test "tile/base/exposed_var_test.cc")
tile_add_test(fiber_detail_scheduler_test "tile/fiber/detail/scheduler_test.cc")
tile_add_test(base_internal_meta_test "tile/base/internal/meta_test.cc")
# tile_add_test(net_internal_http_engine_test

View File

@ -1,7 +0,0 @@
#ifndef TILE__THIRD_PARTY_JSON_JSON_H
#define TILE__THIRD_PARTY_JSON_JSON_H
#pragma once
#include "nlohmann/json.hpp"
#endif // TILE__THIRD_PARTY_JSON_JSON_H

File diff suppressed because it is too large Load Diff

300
tile/base/exposed_var.cc Normal file
View File

@ -0,0 +1,300 @@
#include "tile/base/exposed_var.h"
#include "tile/base/internal/logging.h"
#include "tile/base/internal/move_on_copy.h"
#include "tile/base/string.h"
namespace tile {
namespace exposed_var {
namespace {
#define CHECK_PATH(path) \
TILE_CHECK((path).size() <= 1 || (path).back() != '/', \
"Invalid path: [{}].", path); \
TILE_CHECK((path).find("//") == tile::Slice::npos, "Invalid path: [{}].", \
path);
#define CHECK_RELATIVE_PATH(path) \
CHECK_PATH(path); \
TILE_CHECK((path).empty() || (path).front() != '/', "Invalid path: [{}].", \
path);
#define CHECK_ABSOLUTE_PATH(path) \
CHECK_PATH(path); \
TILE_CHECK((path).empty() || (path).front() == '/', "Invalid path: [{}]", \
path);
std::pair<Slice, Slice> SplitFirstPart(Slice path) {
auto pos = path.find_first_of('/');
if (pos == tile::Slice::npos) {
return std::make_pair(path, "");
} else {
return std::make_pair(path.substr(0, pos), path.substr(pos + 1));
}
}
std::pair<Slice, Slice> SplitLastPart(Slice path) {
auto pos = path.find_last_of('/');
if (pos == tile::Slice::npos) {
return std::make_pair("", path);
}
return std::make_pair(path.substr(0, pos), path.substr(pos + 1));
}
std::string JoinPath(Slice a, Slice b) {
if (EndsWith(b, "/")) {
b.RemoveSuffix(1);
}
if (EndsWith(a, "/")) {
a.RemoveSuffix(1);
}
if (b.empty()) {
return a.ToString();
}
if (a.empty()) {
return b.ToString();
}
return a.ToString() + "/" + b.ToString();
}
std::string SubstituteEscapedSlashForZero(Slice path) {
TILE_CHECK(path.find('\0') == tile::Slice::npos);
return Replace(path, "\\/", "\0");
}
std::string SubstituteZeroForEscapedSlash(Slice path) {
return Replace(path, "\0", "\\/");
}
std::string UnescapeZeroToPlainSlash(Slice path) {
return Replace(path, "\0", "/");
}
} // namespace
ExposedVarGroup::Handle
ExposedVarGroup::Add(Slice rel_path, std::function<Json::Value()> value) {
auto real_path = SubstituteEscapedSlashForZero(rel_path);
CHECK_RELATIVE_PATH(rel_path);
auto path_and_name = SplitLastPart(real_path);
auto name = path_and_name.second.ToString();
auto moved_func = tile::MakeMoveOnCopy(value);
return CreateUpto(path_and_name.first)
->AddDirect(
name,
[name, moved_func](Slice expected) -> std::optional<Json::Value> {
auto jsv = moved_func.Ref()();
if (expected.empty()) {
return jsv;
}
auto real_path = SubstituteEscapedSlashForZero(expected);
Json::Value *ptr = &jsv;
auto pieces = Split(real_path, '/');
for (auto &&e : pieces) {
auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()});
if (ptr->isObject()) {
if (ptr->isMember(unescaped)) {
ptr = &(*ptr)[unescaped];
} else {
return {};
}
} else if (ptr->isArray()) {
auto index = TryParse<std::size_t>(unescaped);
if (index && *index < ptr->size()) {
ptr = &(*ptr)[static_cast<Json::ArrayIndex>(*index)];
} else {
return {};
}
} else {
return {};
}
}
return *ptr;
});
}
ExposedVarGroup::Handle
ExposedVarGroup::Add(Slice rel_path, ExposedVarDynamicTree *dynamic_tree) {
auto real_path = SubstituteEscapedSlashForZero(rel_path);
CHECK_RELATIVE_PATH(real_path);
auto path_and_name = SplitLastPart(real_path);
auto name = path_and_name.second.ToString();
return CreateUpto(path_and_name.first)
->AddDirect(name, [dynamic_tree](Slice inner_path) {
return dynamic_tree->TryGet(inner_path);
});
}
ExposedVarGroup *ExposedVarGroup::FindOrCreate(Slice abs_path) {
auto real_path = SubstituteEscapedSlashForZero(abs_path);
CHECK_ABSOLUTE_PATH(real_path);
return Root()->CreateUpto(real_path.substr(1));
}
std::optional<Json::Value> ExposedVarGroup::TryGet(Slice abs_path) {
auto real_path = SubstituteEscapedSlashForZero(abs_path);
TILE_CHECK(!real_path.empty());
CHECK_ABSOLUTE_PATH(real_path);
if (real_path == "/") {
return Root()->Dump();
}
Slice left_path;
auto rel_path = real_path.substr(1);
auto parent = Root()->FindLowest(rel_path, &left_path);
auto name_and_rest = SplitFirstPart(left_path);
if (name_and_rest.first.empty()) {
return parent->Dump();
}
auto s = name_and_rest.first.ToString();
std::lock_guard<std::mutex> lk(parent->lock_);
auto iter = parent->leaves_.find(s);
if (iter != parent->leaves_.end()) {
return iter->second(SubstituteZeroForEscapedSlash(name_and_rest.second));
} else {
auto iter = parent->nodes_.find(s);
if (iter != parent->nodes_.end()) {
return iter->second->Dump();
} else {
return {};
}
}
}
ExposedVarGroup::ExposedVarGroup(std::string abs_path)
: abs_path_(std::move(abs_path)) {
CHECK_ABSOLUTE_PATH(abs_path);
}
ExposedVarGroup *ExposedVarGroup::Root() {
static ExposedVarGroup evg("/");
return &evg;
}
const std::string &ExposedVarGroup::AbsolutePath() const { return abs_path_; }
ExposedVarGroup *ExposedVarGroup::FindLowest(Slice rel_path, Slice *left) {
CHECK_RELATIVE_PATH(rel_path);
auto current = this;
while (!rel_path.empty()) {
auto name_and_rest = SplitFirstPart(rel_path);
std::lock_guard<std::mutex> lk(current->lock_);
auto iter = current->nodes_.find(name_and_rest.first.ToString());
if (iter == current->nodes_.end()) {
break;
} else {
current = &*iter->second;
}
rel_path = name_and_rest.second;
}
if (left) {
*left = rel_path;
}
return current;
}
ExposedVarGroup *ExposedVarGroup::CreateUpto(Slice rel_path) {
CHECK_RELATIVE_PATH(rel_path);
Slice left_path;
auto current = FindLowest(rel_path, &left_path);
auto pieces = Split(left_path, '/');
for (auto &&e : pieces) {
auto s = e.ToString();
std::lock_guard<std::mutex> lk(current->lock_);
auto p = JoinPath(current->AbsolutePath(), s);
TILE_CHECK(
current->leaves_.find(s) == current->leaves_.end(),
"Path [{}] has already been used: A value is registered at [{}].",
rel_path, p);
TILE_CHECK(current->nodes_.find(s) == current->nodes_.end());
auto evg = std::unique_ptr<ExposedVarGroup>(new ExposedVarGroup(p));
current->nodes_[s] = std::move(evg);
current = &*(current->nodes_[s]);
}
TILE_CHECK(EndsWith(current->AbsolutePath(), rel_path), "[{}] vs [{}]",
current->AbsolutePath(), rel_path);
return current;
}
ExposedVarGroup::Handle ExposedVarGroup::AddDirect(Slice name, Getter value) {
auto s = name.ToString();
std::lock_guard<std::mutex> lk(lock_);
TILE_CHECK(leaves_.find(s) == leaves_.end(),
"Value [{}] has already been registered at [{}].", name,
AbsolutePath());
TILE_CHECK(nodes_.find(s) == nodes_.end(), "Path [{}] has already been used.",
JoinPath(AbsolutePath(), name));
leaves_[s] = std::move(value);
return Deferred([this, s] {
std::lock_guard<std::mutex> lk(lock_);
TILE_CHECK_EQ(leaves_.erase(s), 1);
});
}
Json::Value ExposedVarGroup::Dump() const {
std::lock_guard<std::mutex> lk(lock_);
Json::Value jsv;
for (auto &&node : nodes_) {
jsv[UnescapeZeroToPlainSlash(node.first)] = node.second->Dump();
}
for (auto &&leave : leaves_) {
jsv[UnescapeZeroToPlainSlash(leave.first)] = *leave.second("");
}
return jsv;
}
ExposedVarDynamicTree::ExposedVarDynamicTree(
Slice rel_path, std::function<Json::Value()> getter,
ExposedVarGroup *parent)
: getter_(std::move(getter)) {
handle_ = parent->Add(rel_path, this);
}
std::optional<Json::Value> ExposedVarDynamicTree::TryGet(Slice rel_path) const {
auto real_path = SubstituteEscapedSlashForZero(rel_path);
auto jsv = getter_();
Json::Value *ptr = &jsv;
auto pieces = Split(real_path, '/');
for (auto &&e : pieces) {
auto unescaped = UnescapeZeroToPlainSlash({e.data(), e.size()});
ptr = &(*ptr)[unescaped];
}
if (ptr->isNull()) {
return {};
}
return *ptr;
}
} // namespace exposed_var
} // namespace tile

View File

@ -3,13 +3,145 @@
#pragma once
#include "inja/inja.h"
#include "tile/base/deferred.h"
#include "tile/base/internal/meta.h"
#include "tile/base/optional.h"
#include "tile/base/slice.h"
#include "json/value.h"
#include <unordered_map>
namespace tile {
namespace exposed_var {
template <typename T> inja::json ToJson(const T &t) {
template <typename T>
auto ToJsonValue(const T &t)
-> enable_if_t<std::is_same<T, Json::Value>::value, Json::Value> {
return t;
}
template <typename T>
auto ToJsonValue(const T &t)
-> enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
Json::Value> {
return Json::Value(static_cast<Json::UInt64>(t));
}
template <typename T>
auto ToJsonValue(const T &t)
-> enable_if_t<std::is_integral<T>::value && !std::is_unsigned<T>::value,
Json::Value> {
return Json::Value(static_cast<Json::Int64>(t));
}
template <typename T>
auto ToJsonValue(const T &t)
-> enable_if_t<std::is_floating_point<T>::value ||
std::is_same<T, std::string>::value ||
std::is_same<T, const char *>::value ||
std::is_same<T, bool>::value,
Json::Value> {
return Json::Value(t);
}
template <typename T>
auto ToJsonValue(const T &t)
-> enable_if_t<std::is_same<std::string,
decltype(format_as(std::declval<T>()))>::value,
Json::Value> {
return Json::Value(format_as(t));
}
template <class T> Json::Value ToJsonValue(const std::atomic<T> &v) {
return ToJsonValue(v.load(std::memory_order_relaxed));
}
class ExposedVarDynamicTree;
class ExposedVarGroup {
public:
using Handle = Deferred;
Handle Add(Slice rel_path, std::function<Json::Value()> value);
Handle Add(Slice rel_path, ExposedVarDynamicTree *dyanmic_tree);
static ExposedVarGroup *FindOrCreate(Slice abs_path);
static std::optional<Json::Value> TryGet(Slice abs_path);
private:
using Getter = std::function<std::optional<Json::Value>(Slice)>;
explicit ExposedVarGroup(std::string abs_path);
const std::string &AbsolutePath() const;
ExposedVarGroup *FindLowest(Slice rel_path, Slice *left);
ExposedVarGroup *CreateUpto(Slice rel_path);
Handle AddDirect(Slice name, Getter getter);
Json::Value Dump() const;
static ExposedVarGroup *Root();
private:
std::string abs_path_;
mutable std::mutex lock_;
std::unordered_map<std::string, std::unique_ptr<ExposedVarGroup>> nodes_;
std::unordered_map<std::string, Getter> leaves_;
};
template <typename T> class ExposedVar {
public:
ExposedVar(Slice rel_path) {
LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/"));
}
template <typename U>
ExposedVar(Slice rel_path, U &&initial_value,
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"))
: obj_(std::forward<U>(initial_value)) {
LinkToParent(rel_path, parent);
}
T *operator->() noexcept { return &obj_; }
T &operator*() noexcept { return obj_; }
private:
void LinkToParent(Slice rel_path, ExposedVarGroup *parent) {
handle_ = parent->Add(rel_path,
[this] { return exposed_var::ToJsonValue(obj_); });
}
private:
T obj_{};
ExposedVarGroup::Handle handle_;
};
template <typename T> class ExposedVarDynamic {
public:
ExposedVarDynamic(
Slice rel_path, std::function<T()> getter,
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"))
: getter_(std::move(getter)) {
handle_ = parent->Add(
rel_path, [this] { return exposed_var::ToJsonValue(getter_()); });
}
private:
std::function<T()> getter_;
ExposedVarGroup::Handle handle_;
};
class ExposedVarDynamicTree {
public:
ExposedVarDynamicTree(
Slice rel_path, std::function<Json::Value()> getter,
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"));
std::optional<Json::Value> TryGet(Slice rel_path) const;
private:
std::function<Json::Value()> getter_;
ExposedVarGroup::Handle handle_;
};
} // namespace exposed_var
} // namespace tile

View File

@ -0,0 +1,6 @@
#include "tile/base/exposed_var.h"
#include "gtest/gtest.h"
#include "json/json.h"
namespace tile {}

View File

@ -91,6 +91,16 @@ public:
constexpr Slice(const Slice &other) : data_(other.data_), len_(other.len_) {}
char front() const {
assert(len_ > 0);
return *data_;
}
char back() const {
assert(len_ > 0);
return data_[len_ - 1];
}
Slice &operator=(const Slice &other) {
data_ = other.data_;
len_ = other.len_;
@ -131,6 +141,8 @@ public:
std::size_t find(Slice s, std::size_t pos = 0) const;
std::size_t find_first_of(Slice s) const { return find(s); }
// TODO: Optimize it
std::size_t find_last_of(Slice s) const {
if (s.empty() || s.size() > size()) {