feat add init config
This commit is contained in:
parent
f13a5ecd39
commit
13cfeb22d3
@ -210,6 +210,8 @@ set(TILE_SRCS
|
||||
"tile/rpc/protocol/http/buffer_io.cc"
|
||||
"tile/rpc/protocol/message.cc"
|
||||
# "tile/rpc/server.cc"
|
||||
"tile/util/config.cc"
|
||||
"tile/util/ini_file_config.cc"
|
||||
)
|
||||
|
||||
if((NOT TILE_HAVE_GETIFADDRS) OR (NOT TILE_HAVE_FREEIFADDRS))
|
||||
@ -366,6 +368,7 @@ if(TILE_BUILD_TESTS)
|
||||
tile_add_custom_test("custom_http_client_test" "tests/http_client_test.cc")
|
||||
# tile_add_test(base_internal_time_keeper_test
|
||||
# "tile/base/internal/time_keeper_test.cc")
|
||||
tile_add_test(tile_util_ini_file_config_test "tile/util/ini_file_config_test.cc")
|
||||
endif(TILE_BUILD_TESTS)
|
||||
|
||||
if(TILE_BUILD_BENCHMARKS)
|
||||
|
@ -10,10 +10,32 @@
|
||||
#include "tile/base/likely.h"
|
||||
|
||||
namespace tile {
|
||||
namespace detail {
|
||||
// Has classof
|
||||
template <typename T, typename Base> struct HasClassofImpl {
|
||||
template <
|
||||
typename TT, typename TBase,
|
||||
typename = enable_if_t<std::is_same<
|
||||
decltype(TT::classof(std::declval<const TBase &>())), bool>::value>>
|
||||
static std::true_type test(int);
|
||||
template <typename TT, typename TBase> static std::false_type test(...);
|
||||
|
||||
static constexpr bool value = decltype(test<T, Base>(0))::value;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename = void> struct CastingTraits {
|
||||
template <typename U> static bool RuntimeTypeCheck(const U &val) {
|
||||
template <typename Base>
|
||||
static auto RuntimeTypeCheck(const Base &val)
|
||||
-> enable_if_t<detail::HasClassofImpl<T, Base>::value, bool> {
|
||||
return T::classof(val);
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
static auto RuntimeTypeCheck(const Base &val)
|
||||
-> enable_if_t<!detail::HasClassofImpl<T, Base>::value, bool> {
|
||||
return dynamic_cast<const T *>(&val) != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class Castable {
|
||||
|
@ -637,6 +637,11 @@ public:
|
||||
size_t message_len);
|
||||
};
|
||||
|
||||
class FileLogSink : public LogSink {
|
||||
public:
|
||||
FileLogSink(const std::string &file_path_template);
|
||||
};
|
||||
|
||||
void AddLogSink(LogSink *dest);
|
||||
void RemoveLogSink(LogSink *dest);
|
||||
void SetStderrLogging(LogSeverity min_severity);
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile/base/casting.h"
|
||||
#include "tile/base/down_cast.h"
|
||||
#include "tile/base/internal/logging.h"
|
||||
#include "tile/base/internal/meta.h"
|
||||
#include "tile/base/internal/utility.h"
|
||||
@ -333,6 +335,12 @@ public:
|
||||
return internal::Exchange(ptr_, nullptr);
|
||||
}
|
||||
|
||||
template <typename U> bool Is() const noexcept { return isa<U>(ptr_); }
|
||||
|
||||
template <typename U> RefPtr<U> As() const noexcept {
|
||||
return RefPtr<U>(ref_ptr, down_cast<U>(ptr_));
|
||||
}
|
||||
|
||||
private:
|
||||
T *ptr_;
|
||||
};
|
||||
|
@ -83,35 +83,30 @@ std::vector<Slice> Split(Slice s, Slice delim, bool keep_empty,
|
||||
|
||||
TILE_CHECK_GE(max_split_parts, 0, "`max_split_parts` must be greater than 0");
|
||||
|
||||
constexpr std::size_t kMaxSplit = -1;
|
||||
constexpr std::size_t kMaxSplit = std::numeric_limits<std::size_t>::max();
|
||||
std::vector<Slice> splited;
|
||||
if (s.empty()) {
|
||||
if (s.empty() || max_split_parts == 1) {
|
||||
return splited;
|
||||
}
|
||||
auto current = s;
|
||||
assert(!delim.empty());
|
||||
int split_count = 0;
|
||||
while (max_split_parts == kMaxSplit || split_count < max_split_parts) {
|
||||
auto pos = current.find(delim);
|
||||
if (pos != 0 || keep_empty) {
|
||||
splited.push_back(current.substr(0, pos));
|
||||
++split_count;
|
||||
auto next_pos = current.find(delim);
|
||||
auto part = current.substr(0, next_pos);
|
||||
if (!part.empty() || keep_empty) {
|
||||
splited.push_back(part);
|
||||
}
|
||||
|
||||
if (pos == Slice::npos) {
|
||||
break;
|
||||
}
|
||||
++split_count;
|
||||
|
||||
current = current.substr(pos + delim.size());
|
||||
if (current.empty()) {
|
||||
if (keep_empty) {
|
||||
splited.push_back("");
|
||||
}
|
||||
if (next_pos == Slice::npos) {
|
||||
break;
|
||||
} else if (split_count + 1 == max_split_parts) {
|
||||
// ++split_count;
|
||||
splited.push_back(current);
|
||||
} else if (split_count + 1 >= max_split_parts) {
|
||||
splited.push_back(current.substr(next_pos + delim.size()));
|
||||
break;
|
||||
} else {
|
||||
current = current.substr(next_pos + delim.size());
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,22 +114,36 @@ std::vector<Slice> Split(Slice s, Slice delim, bool keep_empty,
|
||||
}
|
||||
|
||||
Slice TrimLeft(Slice s, Slice cutset) {
|
||||
while (!s.empty() && cutset.find(Slice(s.data(), 1)) != Slice::npos) {
|
||||
return TrimLeft(s, [&cutset](char c) {
|
||||
return cutset.find(Slice(&c, 1)) != Slice::npos;
|
||||
});
|
||||
}
|
||||
|
||||
Slice TrimRight(Slice s, Slice cutset) {
|
||||
return TrimRight(s, [&cutset](char c) {
|
||||
return cutset.find(Slice(&c, 1)) != Slice::npos;
|
||||
});
|
||||
}
|
||||
|
||||
Slice Trim(Slice s, Slice cutset) {
|
||||
return Trim(s, [&cutset](char c) {
|
||||
return cutset.find(Slice(&c, 1)) != Slice::npos;
|
||||
});
|
||||
}
|
||||
Slice TrimLeft(Slice s, std::function<bool(char)> pred) {
|
||||
while (!s.empty() && pred(s[0])) {
|
||||
s.RemovePrefix(1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Slice TrimRight(Slice s, Slice cutset) {
|
||||
while (!s.empty() &&
|
||||
cutset.find(Slice(s.data() + s.size() - 1, 1)) != Slice::npos) {
|
||||
Slice TrimRight(Slice s, std::function<bool(char)> pred) {
|
||||
while (!s.empty() && pred(s[s.size() - 1])) {
|
||||
s.RemoveSuffix(1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Slice Trim(Slice s, Slice cutset) {
|
||||
return TrimRight(TrimLeft(s, cutset), cutset);
|
||||
Slice Trim(Slice s, std::function<bool(char)> pred) {
|
||||
return TrimRight(TrimLeft(s, pred), pred);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -198,11 +207,23 @@ std::string ToLower(Slice s) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Equals(Slice a, Slice b) {
|
||||
bool Equals(Slice a, Slice b, std::size_t len) {
|
||||
if (a.size() > len) {
|
||||
a.RemoveSuffix(a.size() - len);
|
||||
}
|
||||
if (b.size() > len) {
|
||||
b.RemoveSuffix(b.size() - len);
|
||||
}
|
||||
return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
|
||||
}
|
||||
|
||||
bool EqualsIgnoreCase(Slice a, Slice b) {
|
||||
bool EqualsIgnoreCase(Slice a, Slice b, std::size_t len) {
|
||||
if (a.size() > len) {
|
||||
a.RemoveSuffix(a.size() - len);
|
||||
}
|
||||
if (b.size() > len) {
|
||||
b.RemoveSuffix(b.size() - len);
|
||||
}
|
||||
return a.size() == b.size() &&
|
||||
std::equal(a.begin(), a.end(), b.begin(),
|
||||
[](char a, char b) { return ToLower(a) == ToLower(b); });
|
||||
@ -213,21 +234,27 @@ TryParseTraits<bool>::TryParse(Slice s, bool recognizes_alpbabet_symbol,
|
||||
bool ignore_case) {
|
||||
auto num_opt = tile::TryParse<int>(s);
|
||||
if (num_opt) {
|
||||
if (*num_opt == 0) {
|
||||
return *num_opt != 0;
|
||||
} else {
|
||||
const std::vector<std::string> true_arr{"true", "yes", "on", "y"};
|
||||
const std::vector<std::string> false_arr{"false", "no", "off", "n"};
|
||||
if (s.size() > 5) {
|
||||
return false;
|
||||
} else if (*num_opt == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (EqualsIgnoreCase(s, "y") || EqualsIgnoreCase(s, "yes") ||
|
||||
EqualsIgnoreCase(s, "true")) {
|
||||
return true;
|
||||
} else if (EqualsIgnoreCase(s, "n") || EqualsIgnoreCase(s, "no") ||
|
||||
EqualsIgnoreCase(s, "false")) {
|
||||
return false;
|
||||
auto InArray = [](const std::vector<std::string> &arr, Slice s) {
|
||||
for (auto &&e : arr) {
|
||||
if (EqualsIgnoreCase(e, s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (InArray(true_arr, s)) {
|
||||
return true;
|
||||
} else if (InArray(false_arr, s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
|
@ -34,14 +34,19 @@ std::string
|
||||
Replace(Slice str, Slice from, Slice to,
|
||||
std::size_t count = std::numeric_limits<std::size_t>::max());
|
||||
|
||||
Slice TrimLeft(Slice s, Slice cutset = " \t");
|
||||
Slice TrimRight(Slice s, Slice cutset = " \t");
|
||||
Slice Trim(Slice s, Slice cutset = " \t");
|
||||
Slice TrimLeft(Slice s, Slice cutset);
|
||||
Slice TrimRight(Slice s, Slice cutset);
|
||||
Slice Trim(Slice s, Slice cutset);
|
||||
Slice TrimLeft(Slice s, std::function<bool(char)> pred = isspace);
|
||||
Slice TrimRight(Slice s, std::function<bool(char)> pred = isspace);
|
||||
Slice Trim(Slice s, std::function<bool(char)> pred = isspace);
|
||||
|
||||
std::vector<Slice> Split(Slice s, char delim, bool keep_empty = false,
|
||||
std::size_t max_split_parts = -1);
|
||||
std::vector<Slice> Split(Slice s, Slice delim, bool keep_empty = false,
|
||||
std::size_t max_split_parts = -1);
|
||||
std::vector<Slice>
|
||||
Split(Slice s, char delim, bool keep_empty = false,
|
||||
std::size_t max_split_parts = std::numeric_limits<std::size_t>::max());
|
||||
std::vector<Slice>
|
||||
Split(Slice s, Slice delim, bool keep_empty = false,
|
||||
std::size_t max_split_parts = std::numeric_limits<std::size_t>::max());
|
||||
|
||||
std::string Join(const std::vector<std::string> &parts, Slice delim);
|
||||
std::string Join(const std::vector<Slice> &parts, Slice delim);
|
||||
@ -54,8 +59,11 @@ void ToLower(std::string *str);
|
||||
std::string ToUpper(Slice s);
|
||||
std::string ToLower(Slice s);
|
||||
|
||||
bool Equals(Slice a, Slice b);
|
||||
bool EqualsIgnoreCase(Slice a, Slice b);
|
||||
bool Equals(Slice a, Slice b,
|
||||
std::size_t len = std::numeric_limits<std::size_t>::max());
|
||||
bool EqualsIgnoreCase(
|
||||
Slice a, Slice b,
|
||||
std::size_t len = std::numeric_limits<std::size_t>::max());
|
||||
|
||||
// TryParse
|
||||
template <typename T, typename = void> struct TryParseTraits;
|
||||
@ -73,6 +81,10 @@ template <typename T, typename> struct TryParseTraits {
|
||||
};
|
||||
|
||||
// String to bool
|
||||
// 1. "true" or "false"
|
||||
// 2. "yes" or "no"
|
||||
// 3. "on" or "off"
|
||||
// 4. non-zero or zero
|
||||
template <> struct TryParseTraits<bool> {
|
||||
static std::optional<bool> TryParse(Slice s,
|
||||
bool recognizes_alpbabet_symbol = true,
|
||||
|
@ -102,7 +102,7 @@ TEST(String, TryParseFloatingPoint) {
|
||||
TEST(String, TryParseBool) {
|
||||
ASSERT_FALSE(TryParse<bool>(""));
|
||||
ASSERT_FALSE(TryParse<bool>(".."));
|
||||
ASSERT_FALSE(TryParse<bool>("2"));
|
||||
ASSERT_TRUE(TryParse<bool>("2"));
|
||||
|
||||
ASSERT_TRUE(TryParse<bool>("1"));
|
||||
ASSERT_TRUE(TryParse<bool>("0"));
|
||||
|
@ -24,7 +24,7 @@ void EventLoop::Barrier() {}
|
||||
void EventLoop::Run() {}
|
||||
void EventLoop::Stop() {}
|
||||
void EventLoop::Join() {}
|
||||
EventLoop *EventLoop::Current() {}
|
||||
EventLoop *EventLoop::Current() { return nullptr; }
|
||||
void EventLoop::WaitAndRunEvents(std::chrono::milliseconds wait_for) {}
|
||||
void EventLoop::RunUserTasks() {}
|
||||
} // namespace tile
|
||||
|
@ -1,71 +0,0 @@
|
||||
#ifndef TILE_SIGSLOT_SIGNAL_H
|
||||
#define TILE_SIGSLOT_SIGNAL_H
|
||||
|
||||
#pragma once
|
||||
#include "tile/base/internal/meta.h"
|
||||
#include <functional>
|
||||
#include <set>
|
||||
|
||||
/**
|
||||
* tile::sigslot::Signal<void(int)> signal;
|
||||
**/
|
||||
|
||||
namespace tile {
|
||||
namespace sigslot {
|
||||
|
||||
class NonLock {
|
||||
public:
|
||||
void Lock() {}
|
||||
void Unlock() {}
|
||||
};
|
||||
|
||||
class GlobalLock {
|
||||
void Lock();
|
||||
void Unlock();
|
||||
};
|
||||
|
||||
class ThreadedLock {
|
||||
void Lock();
|
||||
void Unlock();
|
||||
};
|
||||
|
||||
class Connection {
|
||||
public:
|
||||
};
|
||||
|
||||
class Slot {
|
||||
public:
|
||||
Slot() = default;
|
||||
virtual ~Slot() = default;
|
||||
|
||||
private:
|
||||
std::set<Connection> connections_;
|
||||
};
|
||||
|
||||
template <typename> class Signal;
|
||||
template <typename R, typename... Args> class Signal<R(Args...)> {
|
||||
public:
|
||||
using SlotType = std::function<R(Args...)>;
|
||||
|
||||
R Emit(Args... args) const;
|
||||
R operator()(Args... args) const { Emit(std::forward<Args>(args)...); };
|
||||
|
||||
// function ptr
|
||||
Connection Connect(R (*func)(Args...));
|
||||
|
||||
// member function
|
||||
template <typename C> Connection Connect(C *clz, R (C::*mem_fn)(Args...));
|
||||
|
||||
// lambda function
|
||||
Connection Connect(std::function<R(Args...)> lambda_fn);
|
||||
template <typename Functor,
|
||||
typename = enable_if_t<std::is_same<
|
||||
decltype(std::declval<Functor>()(std::declval<Args>()...)),
|
||||
R>::value>>
|
||||
Connection Connect(Functor functor);
|
||||
};
|
||||
|
||||
} // namespace sigslot
|
||||
} // namespace tile
|
||||
|
||||
#endif // TILE_SIGSLOT_SIGNAL_H
|
1682
tile/sigslot/sigslot.h
Normal file
1682
tile/sigslot/sigslot.h
Normal file
File diff suppressed because it is too large
Load Diff
113
tile/util/config.cc
Normal file
113
tile/util/config.cc
Normal file
@ -0,0 +1,113 @@
|
||||
#include "tile/util/config.h"
|
||||
#include "tile/base/thread/unique_lock.h"
|
||||
|
||||
namespace tile {
|
||||
namespace util {
|
||||
bool Config::Has(const Slice &key) const {
|
||||
UniqueLock<tile::Mutex> lock(mutex_);
|
||||
std::string value;
|
||||
return GetRaw(key, &value);
|
||||
}
|
||||
|
||||
void Config::Remove(const Slice &key) {}
|
||||
|
||||
void Config::EnableEvents(bool enable) { events_enabled_ = enable; }
|
||||
bool Config::EventsEnabled() const { return events_enabled_; }
|
||||
|
||||
Config::Keys Config::keys(const Slice &root) const {
|
||||
UniqueLock<tile::Mutex> lock(mutex_);
|
||||
return Enumerate(root);
|
||||
}
|
||||
|
||||
#define TILE_DEFINE_CONFIG_GETTER(type, name) \
|
||||
std::optional<type> Config::Get##name(const Slice &key) const { \
|
||||
std::string value; \
|
||||
if (GetRaw(key, &value)) { \
|
||||
auto opt = TryParseTraits<type>::TryParse(value); \
|
||||
if (opt.has_value()) { \
|
||||
return *opt; \
|
||||
} else { \
|
||||
return std::nullopt; \
|
||||
} \
|
||||
} else { \
|
||||
return std::nullopt; \
|
||||
} \
|
||||
} \
|
||||
type Config::Get##name(const Slice &key, type default_value) const { \
|
||||
auto opt = Get##name(key); \
|
||||
if (opt.has_value()) { \
|
||||
return *opt; \
|
||||
} else { \
|
||||
return default_value; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TILE_DEFINE_CONFIG_SETTER(type, name) \
|
||||
void Config::Set##name(const Slice &key, type value) { \
|
||||
SetRawWithEvent(key, Format("{}", value)); \
|
||||
}
|
||||
|
||||
std::optional<std::string> Config::GetString(const Slice &key) const {
|
||||
std::string value;
|
||||
UniqueLock<tile::Mutex> lock(mutex_);
|
||||
if (GetRaw(key, &value)) {
|
||||
return value;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Config::GetString(const Slice &key,
|
||||
std::string default_value) const {
|
||||
auto opt = GetString(key);
|
||||
return opt.has_value() ? *opt : default_value;
|
||||
}
|
||||
|
||||
void Config::SetString(const Slice &key, const std::string &value) {
|
||||
SetRawWithEvent(key, value);
|
||||
}
|
||||
void Config::SetBool(const Slice &key, bool value) {
|
||||
SetRawWithEvent(key, value ? "true" : "false");
|
||||
}
|
||||
|
||||
TILE_DEFINE_CONFIG_GETTER(int, Int)
|
||||
TILE_DEFINE_CONFIG_GETTER(unsigned int, UInt)
|
||||
TILE_DEFINE_CONFIG_GETTER(int16_t, Int16)
|
||||
TILE_DEFINE_CONFIG_GETTER(int32_t, Int32)
|
||||
TILE_DEFINE_CONFIG_GETTER(int64_t, Int64)
|
||||
TILE_DEFINE_CONFIG_GETTER(uint16_t, UInt16)
|
||||
TILE_DEFINE_CONFIG_GETTER(uint32_t, UInt32)
|
||||
TILE_DEFINE_CONFIG_GETTER(uint64_t, UInt64)
|
||||
TILE_DEFINE_CONFIG_GETTER(double, Double)
|
||||
TILE_DEFINE_CONFIG_GETTER(bool, Bool)
|
||||
|
||||
TILE_DEFINE_CONFIG_SETTER(int, Int)
|
||||
TILE_DEFINE_CONFIG_SETTER(unsigned int, UInt)
|
||||
TILE_DEFINE_CONFIG_SETTER(int16_t, Int16)
|
||||
TILE_DEFINE_CONFIG_SETTER(int32_t, Int32)
|
||||
TILE_DEFINE_CONFIG_SETTER(int64_t, Int64)
|
||||
TILE_DEFINE_CONFIG_SETTER(uint16_t, UInt16)
|
||||
TILE_DEFINE_CONFIG_SETTER(uint32_t, UInt32)
|
||||
TILE_DEFINE_CONFIG_SETTER(uint64_t, UInt64)
|
||||
TILE_DEFINE_CONFIG_SETTER(double, Double)
|
||||
|
||||
void Config::SetRawWithEvent(const Slice &key, const std::string &value) {
|
||||
if (events_enabled_) {
|
||||
OnChanging(key, value);
|
||||
}
|
||||
|
||||
{
|
||||
UniqueLock<tile::Mutex> lock(mutex_);
|
||||
SetRaw(key, value);
|
||||
}
|
||||
|
||||
if (events_enabled_) {
|
||||
OnChanged(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
Config::~Config() {}
|
||||
|
||||
} // namespace util
|
||||
|
||||
} // namespace tile
|
@ -3,8 +3,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile/base/optional.h"
|
||||
#include "tile/base/ref_ptr.h"
|
||||
#include "tile/base/slice.h"
|
||||
#include "tile/base/string.h"
|
||||
#include "tile/base/thread/mutex.h"
|
||||
#include "tile/sigslot/sigslot.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace tile {
|
||||
@ -14,16 +18,42 @@ public:
|
||||
using Keys = std::vector<std::string>;
|
||||
using Ptr = RefPtr<Config>;
|
||||
|
||||
// events
|
||||
// Key, Value
|
||||
sigslot::signal2<const Slice &, const Slice &> OnChanging;
|
||||
// Key, Value
|
||||
sigslot::signal2<const Slice &, const Slice &> OnChanged;
|
||||
// Key
|
||||
sigslot::signal1<const Slice &> OnRemoving;
|
||||
// Key
|
||||
sigslot::signal1<const Slice &> OnRemoved;
|
||||
|
||||
bool Has(const Slice &key) const;
|
||||
void Remove(const Slice &key);
|
||||
void EnableEvents(bool enable = true);
|
||||
bool EventsEnabled() const;
|
||||
|
||||
Keys keys(const Slice &root = "") const;
|
||||
|
||||
#define TILE_DECLARE_CONFIG_GETTER(type, name) \
|
||||
type Get##name(const Slice &key) const; \
|
||||
std::optional<type> Get##name(const Slice &key) const; \
|
||||
type Get##name(const Slice &key, type default_value) const;
|
||||
|
||||
#define TILE_DECLARE_CONFIG_SETTER(type, name) \
|
||||
virtual void Set##name(const Slice &key, type value);
|
||||
|
||||
std::string GetRawString(const Slice &key, const Slice &default_value) const;
|
||||
// std::stirng GetString()
|
||||
// int GetInt()
|
||||
// unsigned int GetUInt()
|
||||
// int16_t GetInt16()
|
||||
// int32_t GetInt32()
|
||||
// int64_t GetInt64()
|
||||
// uint16_t GetUInt16()
|
||||
// uint32_t GetUInt32()
|
||||
// uint64_t GetUInt64()
|
||||
// double GetDouble()
|
||||
// bool GetBool()
|
||||
|
||||
// getters
|
||||
TILE_DECLARE_CONFIG_GETTER(std::string, String)
|
||||
@ -40,6 +70,17 @@ public:
|
||||
|
||||
protected:
|
||||
// setters
|
||||
// SetString(const Slice &key, const std::string &value)
|
||||
// SetInt(const Slice &key, int value)
|
||||
// SetUInt(const Slice &key, unsigned int value)
|
||||
// SetInt16(const Slice &key, int16_t value)
|
||||
// SetInt32(const Slice &key, int32_t value)
|
||||
// SetInt64(const Slice &key, int64_t value)
|
||||
// SetUInt16(const Slice &key, uint16_t value)
|
||||
// SetUInt32(const Slice &key, uint32_t value)
|
||||
// SetUInt64(const Slice &key, uint64_t value)
|
||||
// SetDouble(const Slice &key, double value)
|
||||
// SetBool(const Slice &key, bool value)
|
||||
TILE_DECLARE_CONFIG_SETTER(const std::string &, String)
|
||||
TILE_DECLARE_CONFIG_SETTER(int, Int)
|
||||
TILE_DECLARE_CONFIG_SETTER(unsigned int, UInt)
|
||||
@ -55,9 +96,30 @@ protected:
|
||||
#undef TILE_DECLARE_CONFIG_GETTER
|
||||
#undef TILE_DECLARE_CONFIG_SETTER
|
||||
|
||||
void Remove(const Slice &key);
|
||||
protected:
|
||||
class ScopedLock {
|
||||
public:
|
||||
explicit ScopedLock(const Config &config) : config_(config) {
|
||||
config_.mutex_.Lock();
|
||||
}
|
||||
~ScopedLock() { config_.mutex_.Unlock(); }
|
||||
|
||||
Keys keys(const Slice &key_root = "") const;
|
||||
private:
|
||||
const Config &config_;
|
||||
};
|
||||
|
||||
virtual bool GetRaw(const Slice &key, std::string *value) const = 0;
|
||||
virtual void SetRaw(const Slice &key, const Slice &value) = 0;
|
||||
virtual void RemoveRaw(const Slice &key) = 0;
|
||||
virtual Keys Enumerate(const Slice &range) const = 0;
|
||||
void SetRawWithEvent(const Slice &key, const std::string &value);
|
||||
virtual ~Config();
|
||||
|
||||
friend class std::default_delete<Config>;
|
||||
|
||||
private:
|
||||
mutable Mutex mutex_;
|
||||
bool events_enabled_{false};
|
||||
};
|
||||
} // namespace util
|
||||
} // namespace tile
|
||||
|
144
tile/util/ini_file_config.cc
Normal file
144
tile/util/ini_file_config.cc
Normal file
@ -0,0 +1,144 @@
|
||||
#include "tile/util/ini_file_config.h"
|
||||
#include "tile/base/thread/scoped_lock.h"
|
||||
|
||||
namespace tile {
|
||||
namespace util {
|
||||
|
||||
IniFileConfig::IniFileConfig() {}
|
||||
// IniFileConfig::IniFileConfig(std::istream &istr) { load(istr); }
|
||||
// IniFileConfig::IniFileConfig(const std::string &path) { load(path); }
|
||||
IniFileConfig::~IniFileConfig() {}
|
||||
|
||||
bool IniFileConfig::load(std::istream &istr) {
|
||||
Config::ScopedLock lock(*this);
|
||||
map_.clear();
|
||||
section_key_.clear();
|
||||
while (!istr.eof()) {
|
||||
ParseLine(istr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool IniFileConfig::load(const std::string &path) {
|
||||
std::ifstream istr(path);
|
||||
if (istr.good()) {
|
||||
return load(istr);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool IniFileConfig::GetRaw(const Slice &key, std::string *value) const {
|
||||
auto iter = map_.find(key.ToString());
|
||||
if (iter != map_.end()) {
|
||||
*value = iter->second;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void IniFileConfig::SetRaw(const Slice &key, const Slice &value) {
|
||||
map_[key] = value;
|
||||
}
|
||||
|
||||
void IniFileConfig::RemoveRaw(const Slice &key) {
|
||||
std::string prefix = key;
|
||||
if (!prefix.empty()) {
|
||||
prefix.push_back('.');
|
||||
}
|
||||
IStringMap::iterator it = map_.begin();
|
||||
IStringMap::iterator cur;
|
||||
while (it != map_.end()) {
|
||||
if (EqualsIgnoreCase(cur->first, key) ||
|
||||
EqualsIgnoreCase(cur->first, prefix)) {
|
||||
it = map_.erase(cur);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Config::Keys IniFileConfig::Enumerate(const Slice &key) const {
|
||||
Config::Keys range;
|
||||
std::set<Slice> keys;
|
||||
std::string prefix = key.ToString();
|
||||
if (prefix.empty()) {
|
||||
prefix.push_back('.');
|
||||
}
|
||||
|
||||
std::string::size_type psize = prefix.size();
|
||||
for (const auto &p : map_) {
|
||||
auto &key = p.first;
|
||||
if (EqualsIgnoreCase(key, prefix, psize)) {
|
||||
std::string::size_type end = key.find('.', psize);
|
||||
std::string subkey;
|
||||
if (end == std::string::npos) {
|
||||
subkey = key.substr(psize);
|
||||
} else {
|
||||
subkey = key.substr(psize, end - psize);
|
||||
}
|
||||
|
||||
if (keys.find(subkey) == keys.end()) {
|
||||
keys.insert(subkey);
|
||||
range.push_back(subkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
void IniFileConfig::ParseLine(std::istream &istr) {
|
||||
static const int eof = std::char_traits<char>::eof();
|
||||
auto ReadLine = [&](std::string *line) {
|
||||
line->clear();
|
||||
while (true) {
|
||||
int c = istr.get();
|
||||
if (c == eof || c == '\n') {
|
||||
return c != eof;
|
||||
} else {
|
||||
*line += (char)c;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::string raw_line;
|
||||
while (ReadLine(&raw_line)) {
|
||||
if (raw_line.empty()) {
|
||||
continue;
|
||||
}
|
||||
Slice line = TrimLeft(raw_line);
|
||||
if (line.empty() || line[0] == ';' || line[0] == '#') {
|
||||
// skip empty line
|
||||
// skip comment line ; or #
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse section
|
||||
if (line[0] == '[') {
|
||||
auto pos = line.find(']');
|
||||
if (pos == Slice::npos) {
|
||||
section_key_ = Trim(line.substr(1));
|
||||
} else {
|
||||
section_key_ = Trim(line.substr(1, pos - 1));
|
||||
}
|
||||
} else {
|
||||
auto strs = Split(line, "=", true, 2);
|
||||
std::string full_key = section_key_;
|
||||
if (!full_key.empty()) {
|
||||
full_key.push_back('.');
|
||||
}
|
||||
full_key.append(Trim(strs[0]));
|
||||
if (strs.size() > 1) {
|
||||
map_[full_key] = Trim(strs[1]);
|
||||
} else {
|
||||
map_[full_key] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool IniFileConfig::ICompare::operator()(const std::string &s1,
|
||||
const std::string &s2) const {
|
||||
auto len = std::min(s1.size(), s2.size());
|
||||
return strncmp(s1.c_str(), s2.c_str(), len) < 0;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace tile
|
40
tile/util/ini_file_config.h
Normal file
40
tile/util/ini_file_config.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef TILE_UTIL_INI_FILE_CONFIG_H
|
||||
#define TILE_UTIL_INI_FILE_CONFIG_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile/util/config.h"
|
||||
#include <map>
|
||||
|
||||
namespace tile {
|
||||
namespace util {
|
||||
class IniFileConfig : public Config {
|
||||
public:
|
||||
using Ptr = RefPtr<IniFileConfig>;
|
||||
|
||||
IniFileConfig();
|
||||
~IniFileConfig() override;
|
||||
// IniFileConfig(std::istream &istr);
|
||||
// IniFileConfig(const std::string &path);
|
||||
bool load(std::istream &istr);
|
||||
bool load(const std::string &path);
|
||||
|
||||
protected:
|
||||
bool GetRaw(const Slice &key, std::string *value) const override;
|
||||
void SetRaw(const Slice &key, const Slice &value) override;
|
||||
void RemoveRaw(const Slice &key) override;
|
||||
Keys Enumerate(const Slice &range) const override;
|
||||
|
||||
private:
|
||||
void ParseLine(std::istream &istr);
|
||||
struct ICompare {
|
||||
bool operator()(const std::string &s1, const std::string &s2) const;
|
||||
};
|
||||
typedef std::map<std::string, std::string, ICompare> IStringMap;
|
||||
IStringMap map_;
|
||||
std::string section_key_;
|
||||
};
|
||||
} // namespace util
|
||||
} // namespace tile
|
||||
|
||||
#endif // TILE_UTIL_INI_FILE_CONFIG_H
|
46
tile/util/ini_file_config_test.cc
Normal file
46
tile/util/ini_file_config_test.cc
Normal file
@ -0,0 +1,46 @@
|
||||
#include "tile/util/ini_file_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
const char *kIniFileConfig = R"(
|
||||
# comment 1
|
||||
; comment 2
|
||||
# commet 3
|
||||
; commment 4
|
||||
a=1
|
||||
[sec1]
|
||||
a=2
|
||||
[sec3]
|
||||
a=3
|
||||
[sec2.kk]
|
||||
a=4
|
||||
)";
|
||||
|
||||
namespace tile {
|
||||
namespace util {
|
||||
|
||||
static_assert(!detail::HasClassofImpl<IniFileConfig, Config>::value, "");
|
||||
|
||||
TEST(IniFileConfig, LoadFromIStream) {
|
||||
std::stringstream ss(kIniFileConfig);
|
||||
Config::Ptr config = MakeRefCounted<IniFileConfig>();
|
||||
ASSERT_FALSE(config->Has("a"));
|
||||
ASSERT_FALSE(config->Has("sec1.a"));
|
||||
ASSERT_FALSE(config->Has("sec3.a"));
|
||||
if (config.Is<IniFileConfig>()) {
|
||||
IniFileConfig::Ptr ini = config.As<IniFileConfig>();
|
||||
ASSERT_TRUE(ini->load(ss));
|
||||
}
|
||||
ASSERT_TRUE(config->Has("a"));
|
||||
ASSERT_TRUE(config->Has("sec1.a"));
|
||||
ASSERT_TRUE(config->Has("sec3.a"));
|
||||
ASSERT_TRUE(config->Has("sec2.kk.a"));
|
||||
|
||||
ASSERT_TRUE(config->GetInt("a"));
|
||||
ASSERT_TRUE(config->GetInt("sec1.a"));
|
||||
ASSERT_EQ(1, *config->GetInt("a"));
|
||||
ASSERT_EQ(2, *config->GetInt("sec1.a"));
|
||||
ASSERT_EQ(3, *config->GetInt("sec3.a"));
|
||||
ASSERT_EQ(4, *config->GetInt("sec2.kk.a"));
|
||||
}
|
||||
} // namespace util
|
||||
} // namespace tile
|
Loading…
Reference in New Issue
Block a user