feat format file
Some checks failed
android / build (push) Failing after 9s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Debug) (push) Failing after 48m20s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Release) (push) Failing after 52m49s
linux-arm-gcc / linux-gcc-arm (Debug) (push) Failing after 25s
linux-arm-gcc / linux-gcc-arm (Release) (push) Failing after 24s
linux-arm-gcc / linux-gcc-armhf (Debug) (push) Failing after 38m9s
linux-arm-gcc / linux-gcc-armhf (Release) (push) Failing after 33m29s
linux-mips-gcc / linux-gcc-mipsel (Debug) (push) Failing after 9m34s
linux-mips-gcc / linux-gcc-mipsel (Release) (push) Failing after 12m14s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Failing after 10m1s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Failing after 12m29s
linux-riscv64-gcc / linux-gcc-riscv64 (Debug) (push) Failing after 10m6s
linux-riscv64-gcc / linux-gcc-riscv64 (Release) (push) Failing after 11m7s
linux-x64-clang / linux-clang (Debug) (push) Failing after 30s
linux-x64-clang / linux-clang (Release) (push) Failing after 41s
linux-x64-gcc / linux-gcc (Debug) (push) Failing after 6m34s
linux-x64-gcc / linux-gcc (Release) (push) Failing after 10m11s
linux-x86-gcc / linux-gcc (Debug) (push) Failing after 6m30s
linux-x86-gcc / linux-gcc (Release) (push) Failing after 10m36s
Some checks failed
android / build (push) Failing after 9s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Debug) (push) Failing after 48m20s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Release) (push) Failing after 52m49s
linux-arm-gcc / linux-gcc-arm (Debug) (push) Failing after 25s
linux-arm-gcc / linux-gcc-arm (Release) (push) Failing after 24s
linux-arm-gcc / linux-gcc-armhf (Debug) (push) Failing after 38m9s
linux-arm-gcc / linux-gcc-armhf (Release) (push) Failing after 33m29s
linux-mips-gcc / linux-gcc-mipsel (Debug) (push) Failing after 9m34s
linux-mips-gcc / linux-gcc-mipsel (Release) (push) Failing after 12m14s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Failing after 10m1s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Failing after 12m29s
linux-riscv64-gcc / linux-gcc-riscv64 (Debug) (push) Failing after 10m6s
linux-riscv64-gcc / linux-gcc-riscv64 (Release) (push) Failing after 11m7s
linux-x64-clang / linux-clang (Debug) (push) Failing after 30s
linux-x64-clang / linux-clang (Release) (push) Failing after 41s
linux-x64-gcc / linux-gcc (Debug) (push) Failing after 6m34s
linux-x64-gcc / linux-gcc (Release) (push) Failing after 10m11s
linux-x86-gcc / linux-gcc (Debug) (push) Failing after 6m30s
linux-x86-gcc / linux-gcc (Release) (push) Failing after 10m36s
This commit is contained in:
parent
64200d42cc
commit
481015e1c6
@ -32,7 +32,7 @@ jobs:
|
||||
- name: install-tools
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y cmake make
|
||||
sudo apt-get install -y cmake make clang-tools
|
||||
- name: configure
|
||||
env:
|
||||
CC: clang
|
||||
|
@ -9,12 +9,12 @@
|
||||
|
||||
namespace tile {
|
||||
namespace {
|
||||
template <typename T> class OwningBufferBlock : public PolymorphicBufferBlock {
|
||||
template<typename T>
|
||||
class OwningBufferBlock : public PolymorphicBufferBlock {
|
||||
public:
|
||||
explicit OwningBufferBlock(T storage) : storage_(std::move(storage)) {}
|
||||
const char *data() const noexcept override {
|
||||
return reinterpret_cast<const char *>(storage_.data());
|
||||
}
|
||||
|
||||
const char *data() const noexcept override { return reinterpret_cast<const char *>(storage_.data()); }
|
||||
|
||||
std::size_t size() const noexcept override { return storage_.size(); }
|
||||
|
||||
@ -24,8 +24,9 @@ private:
|
||||
}// namespace
|
||||
|
||||
namespace detail {
|
||||
void FlattenToSlowSlow(const NoncontiguousBuffer &nb, void *buffer,
|
||||
std::size_t size) {
|
||||
void
|
||||
FlattenToSlowSlow(const NoncontiguousBuffer &nb, void *buffer, std::size_t size)
|
||||
{
|
||||
TILE_CHECK_GT(nb.ByteSize(), size, "No enough data");
|
||||
std::size_t copied = 0;
|
||||
for (auto iter = nb.begin(); iter != nb.end() && copied != size; ++iter) {
|
||||
@ -36,8 +37,8 @@ void FlattenToSlowSlow(const NoncontiguousBuffer &nb, void *buffer,
|
||||
}
|
||||
}// namespace detail
|
||||
|
||||
NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other)
|
||||
: byte_size_(other.byte_size_) {
|
||||
NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other) : byte_size_(other.byte_size_)
|
||||
{
|
||||
for (auto &&e : other.buffers_) {
|
||||
|
||||
auto b = object_pool::Get<NoncontiguousBuffer::buffer_t>().Leak();
|
||||
@ -47,10 +48,9 @@ NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other)
|
||||
}
|
||||
|
||||
NoncontiguousBuffer &
|
||||
NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other) {
|
||||
if (TILE_UNLIKELY(&other == this)) {
|
||||
return *this;
|
||||
}
|
||||
NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other)
|
||||
{
|
||||
if (TILE_UNLIKELY(&other == this)) { return *this; }
|
||||
|
||||
Clear();
|
||||
byte_size_ = other.byte_size_;
|
||||
@ -63,7 +63,9 @@ NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
void NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept {
|
||||
void
|
||||
NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept
|
||||
{
|
||||
TILE_CHECK_LE(bytes, byte_size_, "Skip bytes exceed buffer size");
|
||||
byte_size_ -= bytes;
|
||||
|
||||
@ -80,17 +82,18 @@ void NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void NoncontiguousBuffer::ClearSlow() noexcept {
|
||||
void
|
||||
NoncontiguousBuffer::ClearSlow() noexcept
|
||||
{
|
||||
byte_size_ = 0;
|
||||
while (!buffers_.empty()) {
|
||||
object_pool::Put<buffer_t>(buffers_.pop_front());
|
||||
}
|
||||
while (!buffers_.empty()) { object_pool::Put<buffer_t>(buffers_.pop_front()); }
|
||||
}
|
||||
|
||||
void NoncontiguousBufferBuilder::InitializeNextBlock() {
|
||||
void
|
||||
NoncontiguousBufferBuilder::InitializeNextBlock()
|
||||
{
|
||||
if (current_) {
|
||||
TILE_CHECK(SizeAvailable(),
|
||||
"You should flush current block by `FlushCurrentBlock()`");
|
||||
TILE_CHECK(SizeAvailable(), "You should flush current block by `FlushCurrentBlock()`");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -98,7 +101,9 @@ void NoncontiguousBufferBuilder::InitializeNextBlock() {
|
||||
used_ = 0;
|
||||
}
|
||||
|
||||
void NoncontiguousBufferBuilder::FlushCurrentBlock() {
|
||||
void
|
||||
NoncontiguousBufferBuilder::FlushCurrentBlock()
|
||||
{
|
||||
if (!used_) {
|
||||
// current block is empty
|
||||
return;
|
||||
@ -109,8 +114,9 @@ void NoncontiguousBufferBuilder::FlushCurrentBlock() {
|
||||
current_ = nullptr;
|
||||
}
|
||||
|
||||
void NoncontiguousBufferBuilder::AppendSlow(const void *ptr,
|
||||
std::size_t length) {
|
||||
void
|
||||
NoncontiguousBufferBuilder::AppendSlow(const void *ptr, std::size_t length)
|
||||
{
|
||||
while (length) {
|
||||
auto copying = std::min(length, SizeAvailable());
|
||||
memcpy(data(), ptr, copying);
|
||||
@ -120,24 +126,31 @@ void NoncontiguousBufferBuilder::AppendSlow(const void *ptr,
|
||||
}
|
||||
}
|
||||
|
||||
void NoncontiguousBufferBuilder::AppendCopy(const NoncontiguousBuffer &buffer) {
|
||||
for (auto &&e : buffer) {
|
||||
Append(e.data(), e.size());
|
||||
}
|
||||
void
|
||||
NoncontiguousBufferBuilder::AppendCopy(const NoncontiguousBuffer &buffer)
|
||||
{
|
||||
for (auto &&e : buffer) { Append(e.data(), e.size()); }
|
||||
}
|
||||
|
||||
NoncontiguousBuffer CreateBufferSlow(Slice s) {
|
||||
NoncontiguousBuffer
|
||||
CreateBufferSlow(Slice s)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append(s);
|
||||
return nbb.DestructiveGet();
|
||||
}
|
||||
NoncontiguousBuffer CreateBufferSlow(const void *ptr, std::size_t size) {
|
||||
|
||||
NoncontiguousBuffer
|
||||
CreateBufferSlow(const void *ptr, std::size_t size)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append(ptr, size);
|
||||
return nbb.DestructiveGet();
|
||||
}
|
||||
|
||||
std::string FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes) {
|
||||
std::string
|
||||
FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes)
|
||||
{
|
||||
max_bytes = std::min(max_bytes, nb.ByteSize());
|
||||
std::string rc;
|
||||
std::size_t left = max_bytes;
|
||||
@ -150,25 +163,22 @@ std::string FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::string FlattenSlowUntil(const NoncontiguousBuffer &nb, Slice delim,
|
||||
std::size_t max_bytes) {
|
||||
if (nb.Empty()) {
|
||||
return {};
|
||||
}
|
||||
std::string
|
||||
FlattenSlowUntil(const NoncontiguousBuffer &nb, Slice delim, std::size_t max_bytes)
|
||||
{
|
||||
if (nb.Empty()) { return {}; }
|
||||
|
||||
{
|
||||
Slice current(nb.FirstContiguous().data(), nb.FirstContiguous().size());
|
||||
auto pos = current.find(delim);
|
||||
if (pos != Slice::npos) {
|
||||
auto expected_bytes = std::min(pos + delim.size(), max_bytes);
|
||||
return std::string(nb.FirstContiguous().data(),
|
||||
nb.FirstContiguous().data() + expected_bytes);
|
||||
return std::string(nb.FirstContiguous().data(), nb.FirstContiguous().data() + expected_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
std::string rc;
|
||||
for (auto iter = nb.begin(); iter != nb.end() && rc.size() < max_bytes;
|
||||
++iter) {
|
||||
for (auto iter = nb.begin(); iter != nb.end() && rc.size() < max_bytes; ++iter) {
|
||||
auto old_len = rc.size();
|
||||
rc.append(iter->data(), iter->size());
|
||||
// find delim
|
||||
@ -181,33 +191,32 @@ std::string FlattenSlowUntil(const NoncontiguousBuffer &nb, Slice delim,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc.size() > max_bytes) {
|
||||
rc.erase(rc.begin() + max_bytes, rc.end());
|
||||
}
|
||||
if (rc.size() > max_bytes) { rc.erase(rc.begin() + max_bytes, rc.end()); }
|
||||
return rc;
|
||||
}
|
||||
|
||||
PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size) {
|
||||
PolymorphicBuffer
|
||||
MakeReferencingBuffer(const void *ptr, std::size_t size)
|
||||
{
|
||||
return MakeReferencingBuffer(ptr, size, [] {});
|
||||
}
|
||||
|
||||
PolymorphicBuffer MakeForeignBuffer(std::string buffer) {
|
||||
PolymorphicBuffer
|
||||
MakeForeignBuffer(std::string buffer)
|
||||
{
|
||||
auto size = buffer.size();
|
||||
return PolymorphicBuffer(
|
||||
MakeRefCounted<OwningBufferBlock<std::string>>(std::move(buffer)), 0,
|
||||
size);
|
||||
return PolymorphicBuffer(MakeRefCounted<OwningBufferBlock<std::string>>(std::move(buffer)), 0, size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
PolymorphicBuffer MakeForeignBuffer(std::vector<T> buffer) {
|
||||
PolymorphicBuffer
|
||||
MakeForeignBuffer(std::vector<T> buffer)
|
||||
{
|
||||
auto size = buffer.size() * sizeof(T);
|
||||
return PolymorphicBuffer(
|
||||
MakeRefCounted<OwningBufferBlock<std::vector<T>>>(std::move(buffer)), 0,
|
||||
size);
|
||||
return PolymorphicBuffer(MakeRefCounted<OwningBufferBlock<std::vector<T>>>(std::move(buffer)), 0, size);
|
||||
}
|
||||
|
||||
#define TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(type) \
|
||||
template PolymorphicBuffer MakeForeignBuffer(std::vector<type> buffer)
|
||||
#define TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(type) template PolymorphicBuffer MakeForeignBuffer(std::vector<type> buffer)
|
||||
|
||||
TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(char);
|
||||
TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(signed char);
|
||||
@ -224,17 +233,15 @@ TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(float);
|
||||
TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(double);
|
||||
TILE_INSTANIATE_MAKE_FOREIGN_BUFFER(long double);
|
||||
|
||||
bool StartsWith(NoncontiguousBuffer buffer, Slice prefix) {
|
||||
if (buffer.ByteSize() < prefix.size()) {
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
StartsWith(NoncontiguousBuffer buffer, Slice prefix)
|
||||
{
|
||||
if (buffer.ByteSize() < prefix.size()) { return false; }
|
||||
|
||||
while (!buffer.Empty() && prefix.empty()) {
|
||||
auto first = buffer.FirstContiguous();
|
||||
auto min_len = std::min(first.size(), prefix.size());
|
||||
if (memcmp(first.data(), prefix.data(), min_len) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp(first.data(), prefix.data(), min_len) != 0) { return false; }
|
||||
|
||||
buffer.Skip(min_len);
|
||||
prefix.RemovePrefix(min_len);
|
||||
@ -242,24 +249,23 @@ bool StartsWith(NoncontiguousBuffer buffer, Slice prefix) {
|
||||
return prefix.empty();
|
||||
}
|
||||
|
||||
bool EndsWith(NoncontiguousBuffer buffer, Slice suffix) {
|
||||
bool
|
||||
EndsWith(NoncontiguousBuffer buffer, Slice suffix)
|
||||
{
|
||||
TILE_NOT_IMPLEMENTED("");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix) {
|
||||
if (buffer.ByteSize() < prefix.size()) {
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix)
|
||||
{
|
||||
if (buffer.ByteSize() < prefix.size()) { return false; }
|
||||
|
||||
while (!buffer.Empty() && prefix.empty()) {
|
||||
auto first = buffer.FirstContiguous();
|
||||
auto min_len = std::min(first.size(), prefix.size());
|
||||
|
||||
if (!EqualsIgnoreCase(first.substr(0, min_len),
|
||||
prefix.substr(0, min_len))) {
|
||||
return false;
|
||||
}
|
||||
if (!EqualsIgnoreCase(first.substr(0, min_len), prefix.substr(0, min_len))) { return false; }
|
||||
|
||||
buffer.Skip(min_len);
|
||||
prefix.RemovePrefix(min_len);
|
||||
@ -267,7 +273,9 @@ bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix) {
|
||||
return prefix.empty();
|
||||
}
|
||||
|
||||
bool EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix) {
|
||||
bool
|
||||
EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix)
|
||||
{
|
||||
TILE_NOT_IMPLEMENTED("");
|
||||
}
|
||||
|
||||
|
@ -20,40 +20,54 @@
|
||||
namespace tile {
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
constexpr auto data(const T &c)
|
||||
-> enable_if_t<std::is_convertible<decltype(c.data()), const char *>::value,
|
||||
const char *> {
|
||||
constexpr auto
|
||||
data(const T &c) -> enable_if_t<std::is_convertible<decltype(c.data()), const char *>::value, const char *>
|
||||
{
|
||||
return c.data();
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
constexpr auto data(const char (&c)[N]) -> decltype(c) {
|
||||
constexpr auto
|
||||
data(const char (&c)[N]) -> decltype(c)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename T> constexpr std::size_t size(const T &c) {
|
||||
template<typename T>
|
||||
constexpr std::size_t
|
||||
size(const T &c)
|
||||
{
|
||||
return c.size();
|
||||
}
|
||||
|
||||
template <std::size_t N> struct size_impl {
|
||||
static constexpr std::size_t size(const char (&c)[N]) {
|
||||
return N - (c[N - 1] == '\0');
|
||||
}
|
||||
template<std::size_t N>
|
||||
struct size_impl {
|
||||
static constexpr std::size_t size(const char (&c)[N]) { return N - (c[N - 1] == '\0'); }
|
||||
};
|
||||
template <> struct size_impl<0> {
|
||||
|
||||
template<>
|
||||
struct size_impl<0> {
|
||||
static constexpr std::size_t size(const char (&c)[0]) { return 0; }
|
||||
};
|
||||
|
||||
template <std::size_t N> constexpr std::size_t size(const char (&c)[N]) {
|
||||
template<std::size_t N>
|
||||
constexpr std::size_t
|
||||
size(const char (&c)[N])
|
||||
{
|
||||
return size_impl<N>::size(c);
|
||||
}
|
||||
|
||||
template <typename A1> constexpr std::size_t total_size(const A1 &a1) {
|
||||
template<typename A1>
|
||||
constexpr std::size_t
|
||||
total_size(const A1 &a1)
|
||||
{
|
||||
return size(a1);
|
||||
}
|
||||
|
||||
template<typename A1, typename... Args>
|
||||
constexpr std::size_t total_size(const A1 &a1, const Args &...args) {
|
||||
constexpr std::size_t
|
||||
total_size(const A1 &a1, const Args &...args)
|
||||
{
|
||||
return size(a1) + total_size(args...);
|
||||
}
|
||||
|
||||
@ -61,8 +75,7 @@ constexpr std::size_t total_size(const A1 &a1, const Args &...args) {
|
||||
|
||||
class NoncontiguousBuffer {
|
||||
// <buffer_t, buffer_t::chain>
|
||||
using LinkedBuffers =
|
||||
internal::SinglyLinkedList<PolymorphicBuffer, &PolymorphicBuffer::chain>;
|
||||
using LinkedBuffers = internal::SinglyLinkedList<PolymorphicBuffer, &PolymorphicBuffer::chain>;
|
||||
|
||||
public:
|
||||
using iterator = LinkedBuffers::iterator;
|
||||
@ -74,13 +87,15 @@ public:
|
||||
// Copyable & moveable
|
||||
NoncontiguousBuffer(const NoncontiguousBuffer &other);
|
||||
NoncontiguousBuffer &operator=(const NoncontiguousBuffer &other);
|
||||
|
||||
NoncontiguousBuffer(NoncontiguousBuffer &&other) noexcept
|
||||
: byte_size_(internal::Exchange(other.byte_size_, 0)),
|
||||
buffers_(std::move(other.buffers_)) {}
|
||||
NoncontiguousBuffer &operator=(NoncontiguousBuffer &&other) noexcept {
|
||||
if (TILE_UNLIKELY(&other == this)) {
|
||||
return *this;
|
||||
}
|
||||
buffers_(std::move(other.buffers_))
|
||||
{}
|
||||
|
||||
NoncontiguousBuffer &operator=(NoncontiguousBuffer &&other) noexcept
|
||||
{
|
||||
if (TILE_UNLIKELY(&other == this)) { return *this; }
|
||||
|
||||
Clear();
|
||||
std::swap(byte_size_, other.byte_size_);
|
||||
@ -90,13 +105,15 @@ public:
|
||||
|
||||
~NoncontiguousBuffer() { Clear(); }
|
||||
|
||||
Slice FirstContiguous() const noexcept {
|
||||
Slice FirstContiguous() const noexcept
|
||||
{
|
||||
TILE_DCHECK(!Empty());
|
||||
auto &&first = buffers_.front();
|
||||
return Slice(first.data(), first.size());
|
||||
}
|
||||
|
||||
void Skip(size_t bytes) noexcept {
|
||||
void Skip(size_t bytes) noexcept
|
||||
{
|
||||
TILE_DCHECK_LE(bytes, ByteSize());
|
||||
if (TILE_UNLIKELY(bytes == 0)) {
|
||||
} else if (bytes < buffers_.front().size()) {
|
||||
@ -107,7 +124,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
NoncontiguousBuffer Cut(std::size_t bytes) {
|
||||
NoncontiguousBuffer Cut(std::size_t bytes)
|
||||
{
|
||||
TILE_DCHECK_LE(bytes, ByteSize());
|
||||
TILE_DCHECK_GE(bytes, 0);
|
||||
|
||||
@ -130,10 +148,9 @@ public:
|
||||
return rc;
|
||||
}
|
||||
|
||||
void Append(PolymorphicBuffer buffer) {
|
||||
if (TILE_UNLIKELY(buffer.size() == 0)) {
|
||||
return;
|
||||
}
|
||||
void Append(PolymorphicBuffer buffer)
|
||||
{
|
||||
if (TILE_UNLIKELY(buffer.size() == 0)) { return; }
|
||||
|
||||
auto block = object_pool::Get<PolymorphicBuffer>();
|
||||
*block = std::move(buffer);
|
||||
@ -141,27 +158,31 @@ public:
|
||||
buffers_.push_back(block.Leak());
|
||||
}
|
||||
|
||||
void Append(NoncontiguousBuffer buffer) {
|
||||
void Append(NoncontiguousBuffer buffer)
|
||||
{
|
||||
byte_size_ += internal::Exchange(buffer.byte_size_, 0);
|
||||
buffers_.splice(std::move(buffer.buffers_));
|
||||
}
|
||||
|
||||
std::size_t ByteSize() const noexcept { return byte_size_; }
|
||||
|
||||
bool Empty() const noexcept {
|
||||
bool Empty() const noexcept
|
||||
{
|
||||
TILE_CHECK_EQ(buffers_.empty(), !byte_size_);
|
||||
return !byte_size_;
|
||||
}
|
||||
|
||||
void Clear() noexcept {
|
||||
if (!Empty()) {
|
||||
ClearSlow();
|
||||
}
|
||||
void Clear() noexcept
|
||||
{
|
||||
if (!Empty()) { ClearSlow(); }
|
||||
}
|
||||
|
||||
iterator begin() noexcept { return buffers_.begin(); }
|
||||
|
||||
iterator end() noexcept { return buffers_.end(); }
|
||||
|
||||
const_iterator begin() const noexcept { return buffers_.begin(); }
|
||||
|
||||
const_iterator end() const noexcept { return buffers_.end(); }
|
||||
|
||||
private:
|
||||
@ -183,11 +204,10 @@ public:
|
||||
// It's size is available at `SizeAvailable()`
|
||||
char *data() const noexcept { return current_->mutable_data() + used_; }
|
||||
|
||||
std::size_t SizeAvailable() const noexcept {
|
||||
return current_->size() - used_;
|
||||
}
|
||||
std::size_t SizeAvailable() const noexcept { return current_->size() - used_; }
|
||||
|
||||
void MarkWritten(std::size_t bytes) {
|
||||
void MarkWritten(std::size_t bytes)
|
||||
{
|
||||
TILE_DCHECK_LE(bytes, SizeAvailable(), "You're overflowing the buffer.");
|
||||
used_ += bytes;
|
||||
// Is fulled ?
|
||||
@ -199,11 +219,10 @@ public:
|
||||
|
||||
// Allocate new block
|
||||
// return write ptr
|
||||
char *Reserve(std::size_t bytes) {
|
||||
char *Reserve(std::size_t bytes)
|
||||
{
|
||||
static const auto kMaxBytes = 1024;
|
||||
TILE_CHECK_LE(bytes, kMaxBytes,
|
||||
"At most [{}] bytes may be reserved in single call.",
|
||||
kMaxBytes);
|
||||
TILE_CHECK_LE(bytes, kMaxBytes, "At most [{}] bytes may be reserved in single call.", kMaxBytes);
|
||||
if (SizeAvailable() < bytes) {
|
||||
FlushCurrentBlock();
|
||||
InitializeNextBlock();
|
||||
@ -217,12 +236,14 @@ public:
|
||||
// Total number of bytes written
|
||||
std::size_t ByteSize() const noexcept { return nb_.ByteSize() + used_; }
|
||||
|
||||
NoncontiguousBuffer DestructiveGet() noexcept {
|
||||
NoncontiguousBuffer DestructiveGet() noexcept
|
||||
{
|
||||
FlushCurrentBlock();
|
||||
return std::move(nb_);
|
||||
}
|
||||
|
||||
void Append(const void *ptr, std::size_t length) {
|
||||
void Append(const void *ptr, std::size_t length)
|
||||
{
|
||||
auto current = data();
|
||||
used_ += length;
|
||||
if (TILE_LIKELY(used_ < current_->size())) {
|
||||
@ -237,9 +258,9 @@ public:
|
||||
|
||||
void Append(const std::string &s) { Append(s.data(), s.size()); }
|
||||
|
||||
void Append(PolymorphicBuffer buffer) {
|
||||
if (buffer.size() < kAppendViaCopyThreshold &&
|
||||
SizeAvailable() >= buffer.size()) {
|
||||
void Append(PolymorphicBuffer buffer)
|
||||
{
|
||||
if (buffer.size() < kAppendViaCopyThreshold && SizeAvailable() >= buffer.size()) {
|
||||
Append(buffer.data(), buffer.size());
|
||||
return;
|
||||
}
|
||||
@ -252,7 +273,8 @@ public:
|
||||
nb_.Append(std::move(buffer));
|
||||
}
|
||||
|
||||
void Append(NoncontiguousBuffer buffer) {
|
||||
void Append(NoncontiguousBuffer buffer)
|
||||
{
|
||||
if (buffer.ByteSize() < kAppendViaCopyThreshold && SizeAvailable()) {
|
||||
AppendCopy(buffer);
|
||||
return;
|
||||
@ -267,19 +289,18 @@ public:
|
||||
|
||||
void Append(char c) { Append(static_cast<unsigned char>(c)); }
|
||||
|
||||
void Append(unsigned char c) {
|
||||
void Append(unsigned char c)
|
||||
{
|
||||
TILE_DCHECK(SizeAvailable());
|
||||
*reinterpret_cast<unsigned char *>(data()) = c;
|
||||
MarkWritten(1);
|
||||
}
|
||||
|
||||
template <
|
||||
typename... Ts,
|
||||
typename =
|
||||
internal::void_t<decltype(detail::data(std::declval<Ts &>()))...>,
|
||||
typename =
|
||||
internal::void_t<decltype(detail::size(std::declval<Ts &>()))...>>
|
||||
void Append(const Ts &...buffers) {
|
||||
template<typename... Ts,
|
||||
typename = internal::void_t<decltype(detail::data(std::declval<Ts &>()))...>,
|
||||
typename = internal::void_t<decltype(detail::size(std::declval<Ts &>()))...>>
|
||||
void Append(const Ts &...buffers)
|
||||
{
|
||||
auto current = data();
|
||||
auto total = detail::total_size(buffers...);
|
||||
used_ += total;
|
||||
@ -292,7 +313,8 @@ public:
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto Append(T) -> enable_if_t<std::is_same<T, int>::value> {
|
||||
auto Append(T) -> enable_if_t<std::is_same<T, int>::value>
|
||||
{
|
||||
static_assert(sizeof(T) == 0, "Please use Append(char) instead.");
|
||||
}
|
||||
|
||||
@ -306,12 +328,14 @@ private:
|
||||
void AppendCopy(const NoncontiguousBuffer &buffer);
|
||||
|
||||
template<typename T, typename...>
|
||||
inline void UncheckedAppend(char *ptr, const T &slice) {
|
||||
inline void UncheckedAppend(char *ptr, const T &slice)
|
||||
{
|
||||
memcpy(ptr, detail::data(slice), detail::size(slice));
|
||||
}
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
inline void UncheckedAppend(char *ptr, const T &slice, const Ts &...slices) {
|
||||
inline void UncheckedAppend(char *ptr, const T &slice, const Ts &...slices)
|
||||
{
|
||||
memcpy(ptr, detail::data(slice), detail::size(slice));
|
||||
UncheckedAppend(ptr + detail::size(slice), slices...);
|
||||
}
|
||||
@ -323,21 +347,20 @@ private:
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
void FlattenToSlowSlow(const NoncontiguousBuffer &ncb, void *buffer,
|
||||
std::size_t size);
|
||||
void FlattenToSlowSlow(const NoncontiguousBuffer &ncb, void *buffer, std::size_t size);
|
||||
}
|
||||
|
||||
NoncontiguousBuffer CreateBufferSlow(Slice s);
|
||||
NoncontiguousBuffer CreateBufferSlow(const void *ptr, std::size_t size);
|
||||
|
||||
std::string
|
||||
FlattenSlow(const NoncontiguousBuffer &nb,
|
||||
std::string FlattenSlow(const NoncontiguousBuffer &nb, std::size_t max_bytes = std::numeric_limits<std::size_t>::max());
|
||||
std::string FlattenSlowUntil(const NoncontiguousBuffer &nb,
|
||||
Slice delim,
|
||||
std::size_t max_bytes = std::numeric_limits<std::size_t>::max());
|
||||
std::string FlattenSlowUntil(
|
||||
const NoncontiguousBuffer &nb, Slice delim,
|
||||
std::size_t max_bytes = std::numeric_limits<std::size_t>::max());
|
||||
inline void FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer,
|
||||
std::size_t max_bytes) {
|
||||
|
||||
inline void
|
||||
FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer, std::size_t max_bytes)
|
||||
{
|
||||
if (TILE_LIKELY(max_bytes <= nb.FirstContiguous().size())) {
|
||||
std::memcpy(buffer, nb.FirstContiguous().data(), max_bytes);
|
||||
}
|
||||
@ -347,15 +370,13 @@ inline void FlattenToSlow(const NoncontiguousBuffer &nb, void *buffer,
|
||||
PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size);
|
||||
|
||||
template<typename F>
|
||||
PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size,
|
||||
F &&completion_cb) {
|
||||
using BufferBlock =
|
||||
ReferencingBufferBlock<typename std::remove_reference<F>::type>;
|
||||
PolymorphicBuffer
|
||||
MakeReferencingBuffer(const void *ptr, std::size_t size, F &&completion_cb)
|
||||
{
|
||||
using BufferBlock = ReferencingBufferBlock<typename std::remove_reference<F>::type>;
|
||||
return PolymorphicBuffer(
|
||||
MakeRefCounted<BufferBlock>(
|
||||
ptr, size,
|
||||
std::forward<typename std::remove_reference<F>::type>(completion_cb)),
|
||||
0, size);
|
||||
MakeRefCounted<BufferBlock>(ptr, size, std::forward<typename std::remove_reference<F>::type>(completion_cb)), 0,
|
||||
size);
|
||||
}
|
||||
|
||||
PolymorphicBuffer MakeForeignBuffer(std::string buffer);
|
||||
|
@ -20,24 +20,28 @@ namespace tile {
|
||||
namespace {
|
||||
|
||||
template<std::size_t kSize>
|
||||
struct alignas(hardware_destructive_interference_size) FixedNativeBufferBlock
|
||||
: NativeBufferBlock {
|
||||
struct alignas(hardware_destructive_interference_size) FixedNativeBufferBlock : NativeBufferBlock {
|
||||
public:
|
||||
static RefPtr<FixedNativeBufferBlock> New() {
|
||||
static RefPtr<FixedNativeBufferBlock> New()
|
||||
{
|
||||
// WARNING: don't use adopt_ptr
|
||||
return RefPtr<FixedNativeBufferBlock<kSize>>(
|
||||
adopt_ptr, object_pool::Get<FixedNativeBufferBlock<kSize>>().Leak());
|
||||
}
|
||||
|
||||
// HACK: add ref ensure `RefCount<Base>`.
|
||||
static void Delete(FixedNativeBufferBlock *ptr) {
|
||||
static void Delete(FixedNativeBufferBlock *ptr)
|
||||
{
|
||||
ptr->Save();
|
||||
object_pool::Put<FixedNativeBufferBlock<kSize>>(ptr);
|
||||
}
|
||||
|
||||
char *mutable_data() noexcept override { return buffer.data(); }
|
||||
|
||||
const char *data() const noexcept override { return buffer.data(); }
|
||||
|
||||
std::size_t size() const noexcept override { return buffer.size(); }
|
||||
|
||||
void Destroy() noexcept override { Delete(this); }
|
||||
|
||||
static constexpr std::size_t kBufferSize = kSize - sizeof(NativeBufferBlock);
|
||||
@ -45,26 +49,27 @@ public:
|
||||
};
|
||||
|
||||
template<std::size_t kSize>
|
||||
RefPtr<NativeBufferBlock> MakeNativeBufferBlockOfBytes() {
|
||||
RefPtr<NativeBufferBlock>
|
||||
MakeNativeBufferBlockOfBytes()
|
||||
{
|
||||
return FixedNativeBufferBlock<kSize>::New();
|
||||
}
|
||||
|
||||
RefPtr<NativeBufferBlock> (*make_native_buffer_block)() =
|
||||
MakeNativeBufferBlockOfBytes<BLOCK_SIZE_4K>;
|
||||
RefPtr<NativeBufferBlock> (*make_native_buffer_block)() = MakeNativeBufferBlockOfBytes<BLOCK_SIZE_4K>;
|
||||
|
||||
void InitializeMakeNativeBufferBlock() {
|
||||
static const std::unordered_map<std::string, RefPtr<NativeBufferBlock> (*)()>
|
||||
kMakers = {{"4K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_4K>},
|
||||
void
|
||||
InitializeMakeNativeBufferBlock()
|
||||
{
|
||||
static const std::unordered_map<std::string, RefPtr<NativeBufferBlock> (*)()> kMakers = {
|
||||
{"4K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_4K> },
|
||||
{"64K", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_64K>},
|
||||
{"1M", MakeNativeBufferBlockOfBytes<BLOCK_SIZE_1M> }
|
||||
|
||||
};
|
||||
auto iter = kMakers.find(FLAGS_tile_buffer_block_size);
|
||||
if (iter != kMakers.end()) {
|
||||
make_native_buffer_block = iter->second;
|
||||
} else {
|
||||
TILE_UNEXPECTED(
|
||||
"Unexpected buffer block size [{}]. Only 4K/64K/1M buffer block "
|
||||
TILE_UNEXPECTED("Unexpected buffer block size [{}]. Only 4K/64K/1M buffer block "
|
||||
"is supported.",
|
||||
FLAGS_tile_buffer_block_size);
|
||||
}
|
||||
@ -74,35 +79,37 @@ TILE_ON_INIT(0, InitializeMakeNativeBufferBlock);
|
||||
|
||||
}// namespace
|
||||
|
||||
RefPtr<NativeBufferBlock> MakeNativeBufferBlock() {
|
||||
RefPtr<NativeBufferBlock>
|
||||
MakeNativeBufferBlock()
|
||||
{
|
||||
return make_native_buffer_block();
|
||||
}
|
||||
|
||||
template <> struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>> {
|
||||
template<>
|
||||
struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>> {
|
||||
static constexpr auto kType = PoolType::MemoryNodeShared;
|
||||
static constexpr auto kLowWaterMark = 16384;// 64M per node.
|
||||
static constexpr auto kHighWaterMark =
|
||||
std::numeric_limits<std::size_t>::max();
|
||||
static constexpr auto kHighWaterMark = std::numeric_limits<std::size_t>::max();
|
||||
static constexpr auto kMaxIdle = std::chrono::seconds(10);
|
||||
static constexpr auto kMinimumThreadCacheSize = 4096;// 16M per thread.
|
||||
static constexpr auto kTransferBatchSize = 1024;// Extra 4M.
|
||||
};
|
||||
|
||||
template <> struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>> {
|
||||
template<>
|
||||
struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>> {
|
||||
static constexpr auto kType = PoolType::MemoryNodeShared;
|
||||
static constexpr auto kLowWaterMark = 1024;// 64M per node.
|
||||
static constexpr auto kHighWaterMark =
|
||||
std::numeric_limits<std::size_t>::max();
|
||||
static constexpr auto kHighWaterMark = std::numeric_limits<std::size_t>::max();
|
||||
static constexpr auto kMaxIdle = std::chrono::seconds(10);
|
||||
static constexpr auto kMinimumThreadCacheSize = 256;// 16M per thread.
|
||||
static constexpr auto kTransferBatchSize = 64; // Extra 4M.
|
||||
};
|
||||
|
||||
template <> struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>> {
|
||||
template<>
|
||||
struct PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>> {
|
||||
static constexpr auto kType = PoolType::MemoryNodeShared;
|
||||
static constexpr auto kLowWaterMark = 128;// 128M per node.
|
||||
static constexpr auto kHighWaterMark =
|
||||
std::numeric_limits<std::size_t>::max();
|
||||
static constexpr auto kHighWaterMark = std::numeric_limits<std::size_t>::max();
|
||||
static constexpr auto kMaxIdle = std::chrono::seconds(10);
|
||||
static constexpr auto kMinimumThreadCacheSize = 64;// 64M per thread.
|
||||
static constexpr auto kTransferBatchSize = 16;// Extra 16M.
|
||||
@ -112,11 +119,8 @@ constexpr PoolType PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>>::kType;
|
||||
constexpr PoolType PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>>::kType;
|
||||
constexpr PoolType PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>>::kType;
|
||||
|
||||
constexpr std::chrono::seconds
|
||||
PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>>::kMaxIdle;
|
||||
constexpr std::chrono::seconds
|
||||
PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>>::kMaxIdle;
|
||||
constexpr std::chrono::seconds
|
||||
PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>>::kMaxIdle;
|
||||
constexpr std::chrono::seconds PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_4K>>::kMaxIdle;
|
||||
constexpr std::chrono::seconds PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_64K>>::kMaxIdle;
|
||||
constexpr std::chrono::seconds PoolTraits<FixedNativeBufferBlock<BLOCK_SIZE_1M>>::kMaxIdle;
|
||||
|
||||
}// namespace tile
|
||||
|
@ -8,8 +8,7 @@
|
||||
#include "tile/base/ref_ptr.h"
|
||||
|
||||
namespace tile {
|
||||
class alignas(hardware_destructive_interference_size) NativeBufferBlock
|
||||
: public PolymorphicBufferBlock {
|
||||
class alignas(hardware_destructive_interference_size) NativeBufferBlock : public PolymorphicBufferBlock {
|
||||
public:
|
||||
virtual char *mutable_data() noexcept = 0;
|
||||
};
|
||||
@ -17,18 +16,17 @@ public:
|
||||
RefPtr<NativeBufferBlock> MakeNativeBufferBlock();
|
||||
|
||||
template<typename F>
|
||||
class ReferencingBufferBlock : public PolymorphicBufferBlock,
|
||||
private F /* Empty Base Optimize */ {
|
||||
class ReferencingBufferBlock : public PolymorphicBufferBlock, private F /* Empty Base Optimize */ {
|
||||
public:
|
||||
explicit ReferencingBufferBlock(const void *ptr, std::size_t size,
|
||||
F &&completion_cb)
|
||||
: F(std::move(completion_cb)), ptr_(ptr), size_(size) {}
|
||||
explicit ReferencingBufferBlock(const void *ptr, std::size_t size, F &&completion_cb)
|
||||
: F(std::move(completion_cb)),
|
||||
ptr_(ptr),
|
||||
size_(size)
|
||||
{}
|
||||
|
||||
~ReferencingBufferBlock() override { (*this)(); }
|
||||
|
||||
const char *data() const noexcept override {
|
||||
return reinterpret_cast<const char *>(ptr_);
|
||||
}
|
||||
const char *data() const noexcept override { return reinterpret_cast<const char *>(ptr_); }
|
||||
|
||||
std::size_t size() const noexcept override { return size_; }
|
||||
|
||||
|
@ -9,26 +9,28 @@
|
||||
#include <vector>
|
||||
|
||||
namespace tile {
|
||||
template <typename T> class CircularBuffer {
|
||||
template<typename T>
|
||||
class CircularBuffer {
|
||||
class UninitializedObject;
|
||||
|
||||
public:
|
||||
explicit CircularBuffer(std::size_t capacity);
|
||||
|
||||
template <typename... Ts> bool Emplace(Ts &&...args) {
|
||||
template<typename... Ts>
|
||||
bool Emplace(Ts &&...args)
|
||||
{
|
||||
auto head = head_.load(std::memory_order_relaxed);
|
||||
auto next = NormalizeIndex(head + 1);
|
||||
|
||||
if (next == tail_.load(std::memory_order_acquire)) {
|
||||
return false;
|
||||
}
|
||||
if (next == tail_.load(std::memory_order_acquire)) { return false; }
|
||||
|
||||
objects_[head].Initialize(std::forward<Ts>(args)...);
|
||||
head_.store(next, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Pop(std::vector<T> *objects) {
|
||||
void Pop(std::vector<T> *objects)
|
||||
{
|
||||
auto upto = head_.load(std::memory_order_acquire);
|
||||
auto current = tail_.load(std::memory_order_relaxed);
|
||||
while (current != upto) {
|
||||
@ -44,7 +46,10 @@ private:
|
||||
class UninitializedObject {
|
||||
public:
|
||||
T *Get() noexcept { return reinterpret_cast<T *>(&storage_); }
|
||||
template <typename... Ts> void Initialize(Ts &&...args) {
|
||||
|
||||
template<typename... Ts>
|
||||
void Initialize(Ts &&...args)
|
||||
{
|
||||
new (Get()) T(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
@ -53,9 +58,8 @@ private:
|
||||
private:
|
||||
std::aligned_storage<sizeof(T), alignof(T)> storage_;
|
||||
};
|
||||
std::size_t NormalizeIndex(std::size_t x) {
|
||||
return (x < capacity_) ? x : x - capacity_;
|
||||
}
|
||||
|
||||
std::size_t NormalizeIndex(std::size_t x) { return (x < capacity_) ? x : x - capacity_; }
|
||||
|
||||
private:
|
||||
std::size_t capacity_;
|
||||
|
@ -2,32 +2,28 @@
|
||||
|
||||
namespace tile {
|
||||
|
||||
NoncontiguousBufferCompressionOutputStream::
|
||||
NoncontiguousBufferCompressionOutputStream(
|
||||
NoncontiguousBufferCompressionOutputStream::NoncontiguousBufferCompressionOutputStream(
|
||||
NoncontiguousBufferBuilder *builder)
|
||||
: builder_(builder) {}
|
||||
: builder_(builder)
|
||||
{}
|
||||
|
||||
NoncontiguousBufferCompressionOutputStream::
|
||||
~NoncontiguousBufferCompressionOutputStream() {
|
||||
Flush();
|
||||
}
|
||||
NoncontiguousBufferCompressionOutputStream::~NoncontiguousBufferCompressionOutputStream() { Flush(); }
|
||||
|
||||
void NoncontiguousBufferCompressionOutputStream::Flush() {
|
||||
void
|
||||
NoncontiguousBufferCompressionOutputStream::Flush()
|
||||
{
|
||||
if (using_bytes_ > 0) {
|
||||
builder_->MarkWritten(using_bytes_);
|
||||
using_bytes_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool NoncontiguousBufferCompressionOutputStream::Next(
|
||||
void **data, std::size_t *size) noexcept {
|
||||
if (!builder_) {
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
NoncontiguousBufferCompressionOutputStream::Next(void **data, std::size_t *size) noexcept
|
||||
{
|
||||
if (!builder_) { return false; }
|
||||
|
||||
if (using_bytes_) {
|
||||
builder_->MarkWritten(using_bytes_);
|
||||
}
|
||||
if (using_bytes_) { builder_->MarkWritten(using_bytes_); }
|
||||
|
||||
*data = builder_->data();
|
||||
*size = builder_->SizeAvailable();
|
||||
@ -36,8 +32,9 @@ bool NoncontiguousBufferCompressionOutputStream::Next(
|
||||
return true;
|
||||
}
|
||||
|
||||
void NoncontiguousBufferCompressionOutputStream::BackUp(
|
||||
std::size_t count) noexcept {
|
||||
void
|
||||
NoncontiguousBufferCompressionOutputStream::BackUp(std::size_t count) noexcept
|
||||
{
|
||||
using_bytes_ -= count;
|
||||
}
|
||||
}// namespace tile
|
||||
|
@ -6,11 +6,9 @@
|
||||
#include "tile/base/compression/compression.h"
|
||||
|
||||
namespace tile {
|
||||
class NoncontiguousBufferCompressionOutputStream
|
||||
: public CompressionOutputStream {
|
||||
class NoncontiguousBufferCompressionOutputStream : public CompressionOutputStream {
|
||||
public:
|
||||
explicit NoncontiguousBufferCompressionOutputStream(
|
||||
NoncontiguousBufferBuilder *builder);
|
||||
explicit NoncontiguousBufferCompressionOutputStream(NoncontiguousBufferBuilder *builder);
|
||||
~NoncontiguousBufferCompressionOutputStream() override;
|
||||
|
||||
void Flush();
|
||||
|
@ -8,12 +8,16 @@ constexpr std::chrono::seconds PoolTraits<PolymorphicBuffer>::kMaxIdle;
|
||||
constexpr std::size_t PoolTraits<PolymorphicBuffer>::kMinimumThreadCacheSize;
|
||||
constexpr std::size_t PoolTraits<PolymorphicBuffer>::kTransferBatchSize;
|
||||
|
||||
void PoolTraits<PolymorphicBuffer>::OnPut(PolymorphicBuffer *bb) {
|
||||
void
|
||||
PoolTraits<PolymorphicBuffer>::OnPut(PolymorphicBuffer *bb)
|
||||
{
|
||||
bb->Clear();
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
void PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p) {
|
||||
void
|
||||
PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p)
|
||||
{
|
||||
TILE_DCHECK_EQ(p->UnsafeRefCount(), 0);
|
||||
p->Destroy();
|
||||
}
|
||||
|
@ -19,21 +19,18 @@ namespace detail {
|
||||
struct PolymorphicBufferBlockDeleter;
|
||||
}
|
||||
|
||||
class PolymorphicBufferBlock
|
||||
: public RefCounted<PolymorphicBufferBlock,
|
||||
detail::PolymorphicBufferBlockDeleter> {
|
||||
class PolymorphicBufferBlock : public RefCounted<PolymorphicBufferBlock, detail::PolymorphicBufferBlockDeleter> {
|
||||
public:
|
||||
virtual ~PolymorphicBufferBlock() = default;
|
||||
virtual const char *data() const noexcept = 0;
|
||||
virtual std::size_t size() const noexcept = 0;
|
||||
|
||||
virtual void Destroy() noexcept { delete this; }
|
||||
};
|
||||
|
||||
static_assert(!std::is_same<detail::as_ref_counted_t<PolymorphicBufferBlock>,
|
||||
PolymorphicBufferBlock>::value,
|
||||
"");
|
||||
static_assert(detail::is_default_ref_traits_safe<PolymorphicBufferBlock>::value,
|
||||
"");
|
||||
static_assert(!std::is_same<detail::as_ref_counted_t<PolymorphicBufferBlock>, PolymorphicBufferBlock>::value, "");
|
||||
static_assert(detail::is_default_ref_traits_safe<PolymorphicBufferBlock>::value, "");
|
||||
|
||||
class PolymorphicBuffer {
|
||||
public:
|
||||
PolymorphicBuffer() = default;
|
||||
@ -41,10 +38,15 @@ public:
|
||||
PolymorphicBuffer &operator=(const PolymorphicBuffer &) = default;
|
||||
|
||||
PolymorphicBuffer(PolymorphicBuffer &&other) noexcept
|
||||
: ptr_(other.ptr_), size_(other.size_), ref_(std::move(other.ref_)) {
|
||||
: ptr_(other.ptr_),
|
||||
size_(other.size_),
|
||||
ref_(std::move(other.ref_))
|
||||
{
|
||||
other.Clear();
|
||||
}
|
||||
PolymorphicBuffer &operator=(PolymorphicBuffer &&other) noexcept {
|
||||
|
||||
PolymorphicBuffer &operator=(PolymorphicBuffer &&other) noexcept
|
||||
{
|
||||
if (this != &other) {
|
||||
ptr_ = other.ptr_;
|
||||
size_ = other.size_;
|
||||
@ -54,26 +56,31 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
PolymorphicBuffer(RefPtr<PolymorphicBufferBlock> data, std::size_t start,
|
||||
std::size_t size)
|
||||
: ptr_(data->data() + start), size_(size), ref_(std::move(data)) {}
|
||||
PolymorphicBuffer(RefPtr<PolymorphicBufferBlock> data, std::size_t start, std::size_t size)
|
||||
: ptr_(data->data() + start),
|
||||
size_(size),
|
||||
ref_(std::move(data))
|
||||
{}
|
||||
|
||||
const char *data() const noexcept { return ptr_; }
|
||||
|
||||
std::size_t size() const noexcept { return size_; }
|
||||
|
||||
void Skip(std::size_t bytes) {
|
||||
void Skip(std::size_t bytes)
|
||||
{
|
||||
TILE_CHECK_LT(bytes, size_);
|
||||
size_ -= bytes;
|
||||
ptr_ += bytes;
|
||||
}
|
||||
|
||||
void set_size(std::size_t size) {
|
||||
void set_size(std::size_t size)
|
||||
{
|
||||
TILE_DCHECK_LE(size, size_);
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
void Reset(RefPtr<PolymorphicBufferBlock> data, std::size_t start,
|
||||
std::size_t size) {
|
||||
void Reset(RefPtr<PolymorphicBufferBlock> data, std::size_t start, std::size_t size)
|
||||
{
|
||||
TILE_DCHECK_LE(start, size);
|
||||
TILE_DCHECK_LE(size, data->size());
|
||||
|
||||
@ -82,7 +89,8 @@ public:
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
void Clear()
|
||||
{
|
||||
ptr_ = nullptr;
|
||||
size_ = 0;
|
||||
ref_ = nullptr;
|
||||
@ -96,6 +104,7 @@ private:
|
||||
std::size_t size_{};
|
||||
RefPtr<PolymorphicBufferBlock> ref_;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct PolymorphicBufferBlockDeleter {
|
||||
void operator()(PolymorphicBufferBlock *p);
|
||||
@ -105,11 +114,11 @@ struct PolymorphicBufferBlockDeleter {
|
||||
}// namespace tile
|
||||
|
||||
namespace tile {
|
||||
template <> struct PoolTraits<PolymorphicBuffer> {
|
||||
template<>
|
||||
struct PoolTraits<PolymorphicBuffer> {
|
||||
static constexpr auto kType = PoolType::MemoryNodeShared;
|
||||
static constexpr std::size_t kLowWaterMark = 32768;
|
||||
static constexpr std::size_t kHighWaterMark =
|
||||
std::numeric_limits<std::size_t>::max();
|
||||
static constexpr std::size_t kHighWaterMark = std::numeric_limits<std::size_t>::max();
|
||||
static constexpr std::chrono::seconds kMaxIdle = std::chrono::seconds(10);
|
||||
static constexpr std::size_t kMinimumThreadCacheSize = 8192;
|
||||
static constexpr std::size_t kTransferBatchSize = 1024;
|
||||
|
@ -14,7 +14,9 @@ namespace tile {
|
||||
|
||||
namespace {
|
||||
|
||||
PolymorphicBuffer MakeNativeBuffer(Slice s) {
|
||||
PolymorphicBuffer
|
||||
MakeNativeBuffer(Slice s)
|
||||
{
|
||||
auto buffer = MakeNativeBufferBlock();
|
||||
memcpy(buffer->mutable_data(), s.data(), s.size());
|
||||
return PolymorphicBuffer(buffer, 0, s.size());
|
||||
@ -22,14 +24,16 @@ PolymorphicBuffer MakeNativeBuffer(Slice s) {
|
||||
|
||||
}// namespace
|
||||
|
||||
TEST(CreateBufferSlow, All) {
|
||||
TEST(CreateBufferSlow, All)
|
||||
{
|
||||
static const auto kData = Slice("sadfas234sadf-/+8sdaf sd f~!#");
|
||||
auto nb = CreateBufferSlow(kData);
|
||||
ASSERT_EQ(kData, nb.FirstContiguous().data());
|
||||
ASSERT_EQ(kData, FlattenSlow(nb));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, Cut) {
|
||||
TEST(NoncontiguousBuffer, Cut)
|
||||
{
|
||||
NoncontiguousBuffer nb;
|
||||
nb.Append(CreateBufferSlow("asdf"));
|
||||
auto r = nb.Cut(3);
|
||||
@ -39,7 +43,8 @@ TEST(NoncontiguousBuffer, Cut) {
|
||||
ASSERT_EQ("asd", FlattenSlow(r));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, Cut1) {
|
||||
TEST(NoncontiguousBuffer, Cut1)
|
||||
{
|
||||
NoncontiguousBuffer nb;
|
||||
nb.Append(CreateBufferSlow("asdf"));
|
||||
auto r = nb.Cut(4);
|
||||
@ -47,7 +52,8 @@ TEST(NoncontiguousBuffer, Cut1) {
|
||||
ASSERT_EQ(4, r.ByteSize());
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, Cut2) {
|
||||
TEST(NoncontiguousBuffer, Cut2)
|
||||
{
|
||||
NoncontiguousBuffer nb;
|
||||
nb.Append(MakeNativeBuffer("asdf"));
|
||||
nb.Append(MakeNativeBuffer("asdf"));
|
||||
@ -56,7 +62,8 @@ TEST(NoncontiguousBuffer, Cut2) {
|
||||
ASSERT_EQ(4, r.ByteSize());
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, Cut3) {
|
||||
TEST(NoncontiguousBuffer, Cut3)
|
||||
{
|
||||
NoncontiguousBuffer nb;
|
||||
nb.Append(MakeNativeBuffer("asdf"));
|
||||
nb.Append(MakeNativeBuffer("asdf"));
|
||||
@ -65,7 +72,8 @@ TEST(NoncontiguousBuffer, Cut3) {
|
||||
ASSERT_EQ(8, r.ByteSize());
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, Cut4) {
|
||||
TEST(NoncontiguousBuffer, Cut4)
|
||||
{
|
||||
auto nb = CreateBufferSlow("asdfasf2345sfsdfdf");
|
||||
auto nb2 = nb;
|
||||
ASSERT_EQ(FlattenSlow(nb), FlattenSlow(nb2));
|
||||
@ -78,7 +86,8 @@ TEST(NoncontiguousBuffer, Cut4) {
|
||||
ASSERT_EQ(FlattenSlow(nb2), FlattenSlow(splited));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, Skip) {
|
||||
TEST(NoncontiguousBuffer, Skip)
|
||||
{
|
||||
NoncontiguousBuffer splited;
|
||||
splited.Append(CreateBufferSlow("asdf"));
|
||||
splited.Append(CreateBufferSlow("asdf"));
|
||||
@ -92,26 +101,30 @@ TEST(NoncontiguousBuffer, Skip) {
|
||||
ASSERT_EQ(0, splited.ByteSize());
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, Skip2) {
|
||||
TEST(NoncontiguousBuffer, Skip2)
|
||||
{
|
||||
NoncontiguousBuffer buffer;
|
||||
EXPECT_TRUE(buffer.Empty());
|
||||
buffer.Skip(0);// Don't crash.
|
||||
EXPECT_TRUE(buffer.Empty());
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, FlattenSlow) {
|
||||
TEST(NoncontiguousBuffer, FlattenSlow)
|
||||
{
|
||||
NoncontiguousBuffer nb;
|
||||
nb.Append(MakeNativeBuffer("asd4234"));
|
||||
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
|
||||
ASSERT_EQ("asd4234aXs", FlattenSlow(nb, 10));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, FlattenToSlow) {
|
||||
TEST(NoncontiguousBuffer, FlattenToSlow)
|
||||
{
|
||||
struct C {
|
||||
std::uint64_t ll;
|
||||
int i;
|
||||
bool f;
|
||||
};
|
||||
|
||||
NoncontiguousBuffer nb;
|
||||
nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78\x9a\xbc\xde\xf0", 8)));
|
||||
nb.Append(MakeNativeBuffer(Slice("\x12\x34\x56\x78", 4)));
|
||||
@ -124,7 +137,8 @@ TEST(NoncontiguousBuffer, FlattenToSlow) {
|
||||
ASSERT_EQ(true, c.f);
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, FlattenSlowUntil) {
|
||||
TEST(NoncontiguousBuffer, FlattenSlowUntil)
|
||||
{
|
||||
NoncontiguousBuffer nb;
|
||||
nb.Append(MakeNativeBuffer("asd4234"));
|
||||
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
|
||||
@ -137,7 +151,8 @@ TEST(NoncontiguousBuffer, FlattenSlowUntil) {
|
||||
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4", 5));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, FlattenSlowUntil2) {
|
||||
TEST(NoncontiguousBuffer, FlattenSlowUntil2)
|
||||
{
|
||||
auto nb = CreateBufferSlow(
|
||||
"HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: "
|
||||
"0\r\nRpc-Error-Reason: The operation completed "
|
||||
@ -151,7 +166,8 @@ TEST(NoncontiguousBuffer, FlattenSlowUntil2) {
|
||||
FlattenSlowUntil(nb, "\r\n\r\n"));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, FlattenSlowUntil3) {
|
||||
TEST(NoncontiguousBuffer, FlattenSlowUntil3)
|
||||
{
|
||||
NoncontiguousBuffer nb;
|
||||
nb.Append(MakeNativeBuffer("asd4234"));
|
||||
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
|
||||
@ -160,14 +176,16 @@ TEST(NoncontiguousBuffer, FlattenSlowUntil3) {
|
||||
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "4aX"));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBuffer, FlattenSlowUntil4) {
|
||||
TEST(NoncontiguousBuffer, FlattenSlowUntil4)
|
||||
{
|
||||
NoncontiguousBuffer nb;
|
||||
nb.Append(MakeNativeBuffer("AB"));
|
||||
nb.Append(MakeNativeBuffer("CDEFGGGGHHHH"));
|
||||
ASSERT_EQ("ABCDEFGGGG", FlattenSlowUntil(nb, "GGGG"));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBufferBuilder, Append) {
|
||||
TEST(NoncontiguousBufferBuilder, Append)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append(MakeForeignBuffer(""));
|
||||
nbb.Append(MakeForeignBuffer("small"));
|
||||
@ -176,11 +194,11 @@ TEST(NoncontiguousBufferBuilder, Append) {
|
||||
nbb.Append(CreateBufferSlow("small"));
|
||||
nbb.Append(CreateBufferSlow(std::string(8192, 'a')));
|
||||
auto nb = nbb.DestructiveGet();
|
||||
EXPECT_EQ("small" + std::string(8192, 'a') + "small" + std::string(8192, 'a'),
|
||||
FlattenSlow(nb));
|
||||
EXPECT_EQ("small" + std::string(8192, 'a') + "small" + std::string(8192, 'a'), FlattenSlow(nb));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBufferBuilder, Reserve) {
|
||||
TEST(NoncontiguousBufferBuilder, Reserve)
|
||||
{
|
||||
auto temp_block = MakeNativeBufferBlock();
|
||||
auto max_bytes = temp_block->size();
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
@ -201,7 +219,8 @@ TEST(NoncontiguousBufferBuilder, Reserve) {
|
||||
ASSERT_EQ(ptr2 + 2, nbb.data());
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet1) {
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet1)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append("asdf1234", 6);
|
||||
nbb.Append("1122", 4);
|
||||
@ -210,45 +229,51 @@ TEST(NoncontiguousBufferBuilder, DestructiveGet1) {
|
||||
FlattenSlow(nbb.DestructiveGet()));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet2) {
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet2)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append("aabbccd");
|
||||
ASSERT_EQ("aabbccd", FlattenSlow(nbb.DestructiveGet()));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet3) {
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet3)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append(std::string(1000000, 'A'));
|
||||
ASSERT_EQ(std::string(1000000, 'A'), FlattenSlow(nbb.DestructiveGet()));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet4) {
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet4)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append('c');
|
||||
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet5) {
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet5)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append(CreateBufferSlow("c"));
|
||||
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
|
||||
}
|
||||
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet6) {
|
||||
TEST(NoncontiguousBufferBuilder, DestructiveGet6)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append(Slice("11"), Slice("2"), std::string("3"), std::string("45"),
|
||||
Slice("6"));
|
||||
nbb.Append(Slice("11"), Slice("2"), std::string("3"), std::string("45"), Slice("6"));
|
||||
nbb.Append("1122", 4);
|
||||
ASSERT_EQ("11234561122", FlattenSlow(nbb.DestructiveGet()));
|
||||
}
|
||||
|
||||
TEST(MakeReferencingBuffer, Simple) {
|
||||
TEST(MakeReferencingBuffer, Simple)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append(MakeReferencingBuffer("abcdefg", 7));
|
||||
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
||||
}
|
||||
|
||||
TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized) {
|
||||
TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized)
|
||||
{
|
||||
int x = 0;
|
||||
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
@ -261,7 +286,8 @@ TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized) {
|
||||
EXPECT_EQ("aaaabcdefg", FlattenSlow(buffer));
|
||||
}
|
||||
|
||||
TEST(MakeReferencingBuffer, WithCallback) {
|
||||
TEST(MakeReferencingBuffer, WithCallback)
|
||||
{
|
||||
static const std::string kBuffer(12345, 'a');
|
||||
int x = 0;
|
||||
|
||||
@ -277,13 +303,15 @@ TEST(MakeReferencingBuffer, WithCallback) {
|
||||
EXPECT_EQ(1, x);
|
||||
}
|
||||
|
||||
TEST(MakeForeignBuffer, String) {
|
||||
TEST(MakeForeignBuffer, String)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append(MakeForeignBuffer(std::string("abcdefg")));
|
||||
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
||||
}
|
||||
|
||||
TEST(MakeForeignBuffer, VectorOfChar) {
|
||||
TEST(MakeForeignBuffer, VectorOfChar)
|
||||
{
|
||||
std::vector<char> data{'a', 'b', 'c', 'd', 'e', 'f', 'g'};
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
nbb.Append(MakeForeignBuffer(std::move(data)));
|
||||
@ -299,7 +327,8 @@ TEST(MakeForeignBuffer, VectorOfChar) {
|
||||
// EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
|
||||
// }
|
||||
|
||||
TEST(MakeForeignBuffer, VectorOfUInt8) {
|
||||
TEST(MakeForeignBuffer, VectorOfUInt8)
|
||||
{
|
||||
std::vector<std::uint8_t> data;
|
||||
data.resize(7);
|
||||
memcpy(data.data(), "abcdefg", 7);
|
||||
|
@ -41,11 +41,27 @@
|
||||
|
||||
namespace tile {
|
||||
|
||||
inline uint16_t ByteSwap16(uint16_t val) { return __builtin_bswap16(val); }
|
||||
inline uint32_t ByteSwap32(uint32_t val) { return __builtin_bswap32(val); }
|
||||
inline uint64_t ByteSwap64(uint64_t val) { return __builtin_bswap64(val); }
|
||||
inline uint16_t
|
||||
ByteSwap16(uint16_t val)
|
||||
{
|
||||
return __builtin_bswap16(val);
|
||||
}
|
||||
|
||||
inline bool IsHostLittleEndian() {
|
||||
inline uint32_t
|
||||
ByteSwap32(uint32_t val)
|
||||
{
|
||||
return __builtin_bswap32(val);
|
||||
}
|
||||
|
||||
inline uint64_t
|
||||
ByteSwap64(uint64_t val)
|
||||
{
|
||||
return __builtin_bswap64(val);
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsHostLittleEndian()
|
||||
{
|
||||
#if TILE_LITTLE_ENDIAN == TILE_BYTE_ORDER
|
||||
return true;
|
||||
#else
|
||||
@ -53,7 +69,9 @@ inline bool IsHostLittleEndian() {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool IsHostBigEndian() {
|
||||
inline bool
|
||||
IsHostBigEndian()
|
||||
{
|
||||
#if TILE_BIG_ENDIAN == TILE_BYTE_ORDER
|
||||
return true;
|
||||
#else
|
||||
@ -61,7 +79,9 @@ inline bool IsHostBigEndian() {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint16_t HostToNetwork16(uint16_t val_in_host) {
|
||||
inline uint16_t
|
||||
HostToNetwork16(uint16_t val_in_host)
|
||||
{
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
return val_in_host;
|
||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||
@ -71,7 +91,9 @@ inline uint16_t HostToNetwork16(uint16_t val_in_host) {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint32_t HostToNetwork32(uint32_t val_in_host) {
|
||||
inline uint32_t
|
||||
HostToNetwork32(uint32_t val_in_host)
|
||||
{
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
return val_in_host;
|
||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||
@ -81,7 +103,9 @@ inline uint32_t HostToNetwork32(uint32_t val_in_host) {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint64_t HostToNetwork64(uint64_t val_in_host) {
|
||||
inline uint64_t
|
||||
HostToNetwork64(uint64_t val_in_host)
|
||||
{
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
return val_in_host;
|
||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||
@ -91,7 +115,9 @@ inline uint64_t HostToNetwork64(uint64_t val_in_host) {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint16_t NetworkToHost16(uint16_t val_in_network) {
|
||||
inline uint16_t
|
||||
NetworkToHost16(uint16_t val_in_network)
|
||||
{
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
return val_in_network;
|
||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||
@ -101,7 +127,9 @@ inline uint16_t NetworkToHost16(uint16_t val_in_network) {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint32_t NetworkToHost32(uint32_t val_in_network) {
|
||||
inline uint32_t
|
||||
NetworkToHost32(uint32_t val_in_network)
|
||||
{
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
return val_in_network;
|
||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||
@ -111,7 +139,9 @@ inline uint32_t NetworkToHost32(uint32_t val_in_network) {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint64_t NetworkToHost64(uint64_t val_in_network) {
|
||||
inline uint64_t
|
||||
NetworkToHost64(uint64_t val_in_network)
|
||||
{
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
return val_in_network;
|
||||
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
|
||||
|
@ -12,25 +12,33 @@ static union EndianHelper {
|
||||
uint32_t v32;
|
||||
uint32_t v64;
|
||||
uint8_t bytes[8];
|
||||
} endian_helper = {.bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
|
||||
} endian_helper = {
|
||||
.bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}
|
||||
};
|
||||
|
||||
TEST(ByteOrder, ByteSwap16) {
|
||||
TEST(ByteOrder, ByteSwap16)
|
||||
{
|
||||
const uint16_t val = 0x1234;
|
||||
const uint16_t expected = 0x3412;
|
||||
ASSERT_EQ(expected, ByteSwap16(val));
|
||||
}
|
||||
TEST(ByteOrder, ByteSwap32) {
|
||||
|
||||
TEST(ByteOrder, ByteSwap32)
|
||||
{
|
||||
const uint32_t val = 0x12345678;
|
||||
const uint32_t expected = 0x78563412;
|
||||
ASSERT_EQ(expected, ByteSwap32(val));
|
||||
}
|
||||
TEST(ByteOrder, ByteSwap64) {
|
||||
|
||||
TEST(ByteOrder, ByteSwap64)
|
||||
{
|
||||
const uint64_t val = 0x1234567890abcdef;
|
||||
const uint64_t expected = 0xefcdab9078563412;
|
||||
ASSERT_EQ(expected, ByteSwap64(val));
|
||||
}
|
||||
|
||||
TEST(ByteOrder, IsHostByteOrder) {
|
||||
TEST(ByteOrder, IsHostByteOrder)
|
||||
{
|
||||
ASSERT_NE(IsHostBigEndian(), IsHostLittleEndian());
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
ASSERT_TRUE(IsHostBigEndian());
|
||||
@ -41,7 +49,8 @@ TEST(ByteOrder, IsHostByteOrder) {
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(ByteOrder, HostToNetwork16) {
|
||||
TEST(ByteOrder, HostToNetwork16)
|
||||
{
|
||||
const uint16_t val = 0x1234;
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
const uint16_t expected = val;
|
||||
@ -53,7 +62,8 @@ TEST(ByteOrder, HostToNetwork16) {
|
||||
ASSERT_EQ(expected, HostToNetwork16(val));
|
||||
}
|
||||
|
||||
TEST(ByteOrder, HostToNetwork32) {
|
||||
TEST(ByteOrder, HostToNetwork32)
|
||||
{
|
||||
const uint32_t val = 0x12345678;
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
const uint32_t expected = val;
|
||||
@ -65,7 +75,8 @@ TEST(ByteOrder, HostToNetwork32) {
|
||||
ASSERT_EQ(expected, HostToNetwork32(val));
|
||||
}
|
||||
|
||||
TEST(ByteOrder, HostToNetwork64) {
|
||||
TEST(ByteOrder, HostToNetwork64)
|
||||
{
|
||||
const uint64_t val = 0x1234567890abcdef;
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
const uint64_t expected = val;
|
||||
@ -77,7 +88,8 @@ TEST(ByteOrder, HostToNetwork64) {
|
||||
ASSERT_EQ(expected, HostToNetwork64(val));
|
||||
}
|
||||
|
||||
TEST(ByteOrder, NetworkToHost16) {
|
||||
TEST(ByteOrder, NetworkToHost16)
|
||||
{
|
||||
const uint16_t val = 0x1234;
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
const uint16_t expected = val;
|
||||
@ -89,7 +101,8 @@ TEST(ByteOrder, NetworkToHost16) {
|
||||
ASSERT_EQ(expected, NetworkToHost16(val));
|
||||
}
|
||||
|
||||
TEST(ByteOrder, NetworkToHost32) {
|
||||
TEST(ByteOrder, NetworkToHost32)
|
||||
{
|
||||
const uint32_t val = 0x12345678;
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
const uint32_t expected = val;
|
||||
@ -101,7 +114,8 @@ TEST(ByteOrder, NetworkToHost32) {
|
||||
ASSERT_EQ(expected, NetworkToHost32(val));
|
||||
}
|
||||
|
||||
TEST(ByteOrder, NetworkToHost64) {
|
||||
TEST(ByteOrder, NetworkToHost64)
|
||||
{
|
||||
const uint64_t val = 0x1234567890abcdef;
|
||||
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
|
||||
const uint64_t expected = val;
|
||||
|
@ -12,28 +12,30 @@
|
||||
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>>
|
||||
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(...);
|
||||
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 T, typename = void>
|
||||
struct CastingTraits {
|
||||
template<typename Base>
|
||||
static auto RuntimeTypeCheck(const Base &val)
|
||||
-> enable_if_t<detail::HasClassofImpl<T, Base>::value, bool> {
|
||||
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> {
|
||||
static auto RuntimeTypeCheck(const Base &val) -> enable_if_t<!detail::HasClassofImpl<T, Base>::value, bool>
|
||||
{
|
||||
return dynamic_cast<const T *>(&val) != nullptr;
|
||||
}
|
||||
};
|
||||
@ -42,27 +44,30 @@ class Castable {
|
||||
class Dummy {};
|
||||
|
||||
public:
|
||||
friend void SetRuntimeType(Castable *ptr, int type, Dummy = {}) {
|
||||
ptr->type_ = type;
|
||||
}
|
||||
friend void SetRuntimeType(Castable *ptr, int type, Dummy = {}) { ptr->type_ = type; }
|
||||
|
||||
friend int GetRuntimeType(const Castable &val, Dummy = {}) {
|
||||
return val.type_;
|
||||
}
|
||||
friend int GetRuntimeType(const Castable &val, Dummy = {}) { return val.type_; }
|
||||
|
||||
protected:
|
||||
template <typename, typename> friend struct CastingTraits;
|
||||
template<typename, typename>
|
||||
friend struct CastingTraits;
|
||||
~Castable() = default;
|
||||
|
||||
template <typename T> void SetRuntimeTypeTo() {
|
||||
template<typename T>
|
||||
void SetRuntimeTypeTo()
|
||||
{
|
||||
SetRuntimeType(this, GetRuntimeTypeId<T>());
|
||||
}
|
||||
|
||||
template <typename T> int GetRuntimeType() const {
|
||||
template<typename T>
|
||||
int GetRuntimeType() const
|
||||
{
|
||||
return GetRuntimeTypeId<T>();
|
||||
}
|
||||
|
||||
template <typename T> static int GetRuntimeTypeId() {
|
||||
template<typename T>
|
||||
static int GetRuntimeTypeId()
|
||||
{
|
||||
static const int value = internal::IndexAlloc::For<Castable>()->Next();
|
||||
return value;
|
||||
}
|
||||
@ -78,45 +83,54 @@ protected:
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct CastingTraits<
|
||||
T, enable_if_t<std::is_base_of<ExactMatchCastable, T>::value>> {
|
||||
static bool RuntimeTypeCheck(const ExactMatchCastable &object) {
|
||||
struct CastingTraits<T, enable_if_t<std::is_base_of<ExactMatchCastable, T>::value>> {
|
||||
static bool RuntimeTypeCheck(const ExactMatchCastable &object)
|
||||
{
|
||||
return GetRuntimeType(object) == Castable::GetRuntimeTypeId<T>();
|
||||
}
|
||||
};
|
||||
|
||||
namespace casting {
|
||||
namespace detail {
|
||||
template <typename T, typename Base,
|
||||
typename = enable_if_t<std::is_base_of<Base, T>::value &&
|
||||
!std::is_same<Base, T>::value>>
|
||||
inline bool RuntimeTypeCheck(const Base &val) {
|
||||
template<typename T,
|
||||
typename Base,
|
||||
typename = enable_if_t<std::is_base_of<Base, T>::value && !std::is_same<Base, T>::value>>
|
||||
inline bool
|
||||
RuntimeTypeCheck(const Base &val)
|
||||
{
|
||||
return CastingTraits<T>::RuntimeTypeCheck(val);
|
||||
}
|
||||
|
||||
template <typename T> inline bool RuntimeTypeCheck(const T &val) {
|
||||
template<typename T>
|
||||
inline bool
|
||||
RuntimeTypeCheck(const T &val)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T, typename Base>
|
||||
using casted_type_t =
|
||||
internal::conditional_t<std::is_const<Base>::value, const T *, T *>;
|
||||
using casted_type_t = internal::conditional_t<std::is_const<Base>::value, const T *, T *>;
|
||||
|
||||
template<typename T, typename Base>
|
||||
auto ReallyCastable(Base *ptr)
|
||||
-> enable_if_t<std::has_virtual_destructor<T>::value, bool> {
|
||||
auto
|
||||
ReallyCastable(Base *ptr) -> enable_if_t<std::has_virtual_destructor<T>::value, bool>
|
||||
{
|
||||
return dynamic_cast<const T *>(ptr) != nullptr;
|
||||
}
|
||||
|
||||
template<typename T, typename Base>
|
||||
auto ReallyCastable(Base *ptr)
|
||||
-> enable_if_t<!std::has_virtual_destructor<T>::value, bool> {
|
||||
auto
|
||||
ReallyCastable(Base *ptr) -> enable_if_t<!std::has_virtual_destructor<T>::value, bool>
|
||||
{
|
||||
return typeid(T) == typeid(*ptr);
|
||||
}
|
||||
|
||||
template <typename T, typename Base> void InvalidCast(Base *ptr) {
|
||||
template<typename T, typename Base>
|
||||
void
|
||||
InvalidCast(Base *ptr)
|
||||
{
|
||||
if (ReallyCastable<T, Base>(ptr)) {
|
||||
TILE_LOG_FATAL(
|
||||
"Casting to type [{}] failed. However, the C++ runtime reports that "
|
||||
TILE_LOG_FATAL("Casting to type [{}] failed. However, the C++ runtime reports that "
|
||||
"you're indeed casting to the (right) runtime type of the object. This "
|
||||
"can happen when either: 1) you haven't initialize object's runtime "
|
||||
"type correctly (e.g. via `SetRuntimeTypeTo` if you're inheriting from "
|
||||
@ -124,8 +138,7 @@ template <typename T, typename Base> void InvalidCast(Base *ptr) {
|
||||
"incorrect.",
|
||||
GetTypeName<T>());
|
||||
} else {
|
||||
TILE_LOG_FATAL(
|
||||
"Invalid cast: Runtime type [{}] expected, got [{}]. If you believe "
|
||||
TILE_LOG_FATAL("Invalid cast: Runtime type [{}] expected, got [{}]. If you believe "
|
||||
"this is an error, check if your `classof` is implemented correctly",
|
||||
GetTypeName<T>(), GetTypeName(*ptr));
|
||||
}
|
||||
@ -135,52 +148,61 @@ template <typename T, typename Base> void InvalidCast(Base *ptr) {
|
||||
}// namespace detail
|
||||
}// namespace casting
|
||||
|
||||
template <typename T, typename Base,
|
||||
typename = enable_if_t<!std::is_pointer<Base>::value>>
|
||||
inline bool isa(const Base &val) {
|
||||
template<typename T, typename Base, typename = enable_if_t<!std::is_pointer<Base>::value>>
|
||||
inline bool
|
||||
isa(const Base &val)
|
||||
{
|
||||
return casting::detail::RuntimeTypeCheck<T>(val);
|
||||
}
|
||||
|
||||
template <typename T, typename Base> inline bool isa(const Base *val) {
|
||||
template<typename T, typename Base>
|
||||
inline bool
|
||||
isa(const Base *val)
|
||||
{
|
||||
return isa<T>(*val);
|
||||
}
|
||||
|
||||
template <typename T, typename Base,
|
||||
typename R = casting::detail::casted_type_t<T, Base>>
|
||||
R dyn_cast(Base *ptr) {
|
||||
template<typename T, typename Base, typename R = casting::detail::casted_type_t<T, Base>>
|
||||
R
|
||||
dyn_cast(Base *ptr)
|
||||
{
|
||||
return isa<T>(ptr) ? static_cast<R>(ptr) : nullptr;
|
||||
}
|
||||
|
||||
template <typename T, typename Base,
|
||||
typename = enable_if_t<!std::is_pointer<Base>::value>>
|
||||
template<typename T, typename Base, typename = enable_if_t<!std::is_pointer<Base>::value>>
|
||||
inline auto
|
||||
dyn_cast(Base &val) -> decltype(dyn_cast<T>(std::declval<Base *>())) {
|
||||
dyn_cast(Base &val) -> decltype(dyn_cast<T>(std::declval<Base *>()))
|
||||
{
|
||||
return dyn_cast<T>(&val);
|
||||
}
|
||||
|
||||
template<typename T, typename Base>
|
||||
inline auto dyn_cast_or_null(Base *ptr) -> decltype(dyn_cast<T>(ptr)) {
|
||||
inline auto
|
||||
dyn_cast_or_null(Base *ptr) -> decltype(dyn_cast<T>(ptr))
|
||||
{
|
||||
return ptr ? dyn_cast<T>(ptr) : nullptr;
|
||||
}
|
||||
|
||||
template <typename T, typename Base,
|
||||
typename R = casting::detail::casted_type_t<T, Base>>
|
||||
inline R cast(Base *ptr) {
|
||||
if (TILE_LIKELY(isa<T>(ptr))) {
|
||||
return static_cast<R>(ptr);
|
||||
}
|
||||
template<typename T, typename Base, typename R = casting::detail::casted_type_t<T, Base>>
|
||||
inline R
|
||||
cast(Base *ptr)
|
||||
{
|
||||
if (TILE_LIKELY(isa<T>(ptr))) { return static_cast<R>(ptr); }
|
||||
casting::detail::InvalidCast<T>(ptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T, typename Base,
|
||||
typename = enable_if_t<!std::is_pointer<Base>::value>>
|
||||
inline auto cast(Base &val) -> decltype(cast<T>(std::declval<Base *>())) {
|
||||
template<typename T, typename Base, typename = enable_if_t<!std::is_pointer<Base>::value>>
|
||||
inline auto
|
||||
cast(Base &val) -> decltype(cast<T>(std::declval<Base *>()))
|
||||
{
|
||||
return cast<T>(&val);
|
||||
}
|
||||
|
||||
template<typename T, typename Base>
|
||||
inline auto cast_or_null(Base *ptr) -> decltype(cast<T>(ptr)) {
|
||||
inline auto
|
||||
cast_or_null(Base *ptr) -> decltype(cast<T>(ptr))
|
||||
{
|
||||
return ptr ? cast<T>(ptr) : nullptr;
|
||||
}
|
||||
|
||||
|
@ -8,15 +8,14 @@ namespace tile {
|
||||
|
||||
struct Base {
|
||||
virtual ~Base() = default;
|
||||
|
||||
enum { kA, kB, kC [[maybe_unused]] } type;
|
||||
};
|
||||
|
||||
struct A : Base {
|
||||
A() { type = kA; }
|
||||
|
||||
static bool classof(const Base &val) {
|
||||
return val.type == kA || val.type == kB;
|
||||
}
|
||||
static bool classof(const Base &val) { return val.type == kA || val.type == kB; }
|
||||
};
|
||||
|
||||
struct B : A {
|
||||
@ -43,26 +42,26 @@ auto pc2 = make_unique<C2>();
|
||||
C1 *pc1 = pc2.get();
|
||||
volatile C3 *pc3;
|
||||
|
||||
void Benchmark_BuiltinDynamicCast(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
converted_ptr = dynamic_cast<A *>(ptr);
|
||||
}
|
||||
void
|
||||
Benchmark_BuiltinDynamicCast(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) { converted_ptr = dynamic_cast<A *>(ptr); }
|
||||
}
|
||||
|
||||
BENCHMARK(Benchmark_BuiltinDynamicCast);
|
||||
|
||||
void Benchmark_DynCast(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
converted_ptr = dyn_cast<A>(ptr);
|
||||
}
|
||||
void
|
||||
Benchmark_DynCast(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) { converted_ptr = dyn_cast<A>(ptr); }
|
||||
}
|
||||
|
||||
BENCHMARK(Benchmark_DynCast);
|
||||
|
||||
void Benchmark_ExactMatchCastableDynCast(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
pc3 = dyn_cast<C3>(pc1);
|
||||
}
|
||||
void
|
||||
Benchmark_ExactMatchCastableDynCast(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) { pc3 = dyn_cast<C3>(pc1); }
|
||||
}
|
||||
|
||||
BENCHMARK(Benchmark_ExactMatchCastableDynCast);
|
||||
|
@ -15,9 +15,7 @@ struct Base {
|
||||
struct A : Base {
|
||||
A() { type = kA; }
|
||||
|
||||
static bool classof(const Base &val) {
|
||||
return val.type == kA || val.type == kB;
|
||||
}
|
||||
static bool classof(const Base &val) { return val.type == kA || val.type == kB; }
|
||||
};
|
||||
|
||||
struct B : A {
|
||||
@ -42,23 +40,27 @@ struct C3 : C1 {
|
||||
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
|
||||
};
|
||||
|
||||
TEST(CastingDeathTest, InvalidCast) {
|
||||
TEST(CastingDeathTest, InvalidCast)
|
||||
{
|
||||
auto pa = make_unique<A>();
|
||||
ASSERT_DEATH(cast<B>(pa.get()), "Invalid cast");
|
||||
}
|
||||
|
||||
TEST(Casting, Nullptr) {
|
||||
TEST(Casting, Nullptr)
|
||||
{
|
||||
B *pb = nullptr;
|
||||
ASSERT_EQ(nullptr, dyn_cast_or_null<A>(pb));
|
||||
ASSERT_EQ(nullptr, cast_or_null<A>(pb));
|
||||
}
|
||||
|
||||
TEST(Casting, DownCastFailure) {
|
||||
TEST(Casting, DownCastFailure)
|
||||
{
|
||||
auto pa = make_unique<A>();
|
||||
ASSERT_EQ(nullptr, dyn_cast<B>(pa.get()));
|
||||
}
|
||||
|
||||
TEST(Casting, Cast) {
|
||||
TEST(Casting, Cast)
|
||||
{
|
||||
auto pb = make_unique<B>();
|
||||
Base *ptr = pb.get();
|
||||
|
||||
@ -75,7 +77,8 @@ TEST(Casting, Cast) {
|
||||
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
|
||||
}
|
||||
|
||||
TEST(Casting, CastWithConst) {
|
||||
TEST(Casting, CastWithConst)
|
||||
{
|
||||
auto pb = make_unique<const B>();
|
||||
const Base *ptr = pb.get();
|
||||
|
||||
@ -92,7 +95,8 @@ TEST(Casting, CastWithConst) {
|
||||
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
|
||||
}
|
||||
|
||||
TEST(Casting, ExactMatchCastable) {
|
||||
TEST(Casting, ExactMatchCastable)
|
||||
{
|
||||
auto pc2 = make_unique<C2>();
|
||||
C1 *p = pc2.get();
|
||||
ASSERT_NE(nullptr, dyn_cast<C1>(p));// Success.
|
||||
|
@ -15,30 +15,34 @@
|
||||
namespace tile {
|
||||
namespace {
|
||||
|
||||
template <typename T> struct Epoch {};
|
||||
template <> struct Epoch<std::chrono::steady_clock> {
|
||||
static constexpr auto value =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::seconds(0));
|
||||
template<typename T>
|
||||
struct Epoch {};
|
||||
|
||||
template<>
|
||||
struct Epoch<std::chrono::steady_clock> {
|
||||
static constexpr auto value = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(0));
|
||||
};
|
||||
|
||||
template <> struct Epoch<std::chrono::system_clock> {
|
||||
template<>
|
||||
struct Epoch<std::chrono::system_clock> {
|
||||
static constexpr auto value =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::seconds(1716543273));
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1716543273));
|
||||
};
|
||||
|
||||
template<typename Clock>
|
||||
inline typename Clock::time_point ReadClock(clockid_t type) {
|
||||
inline typename Clock::time_point
|
||||
ReadClock(clockid_t type)
|
||||
{
|
||||
timespec ts;
|
||||
clock_gettime(type, &ts);
|
||||
const long long ns = ts.tv_sec * 1000LL * 1000LL * 1000LL + ts.tv_nsec;
|
||||
auto duration = std::chrono::duration_cast<typename Clock::duration>(
|
||||
std::chrono::nanoseconds(ns));
|
||||
auto duration = std::chrono::duration_cast<typename Clock::duration>(std::chrono::nanoseconds(ns));
|
||||
return typename Clock::time_point(duration);
|
||||
}
|
||||
|
||||
void Sleep(long ns) {
|
||||
void
|
||||
Sleep(long ns)
|
||||
{
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = (ns + 999) / 1000;
|
||||
@ -47,16 +51,19 @@ void Sleep(long ns) {
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
namespace detail {
|
||||
namespace chrono {
|
||||
struct alignas(hardware_destructive_interference_size)
|
||||
AsynchronouslyUpdatedTimestamps {
|
||||
struct alignas(hardware_destructive_interference_size) AsynchronouslyUpdatedTimestamps {
|
||||
std::atomic<std::chrono::nanoseconds> steady_clock_time_since_epoch;
|
||||
std::atomic<std::chrono::nanoseconds> system_clock_time_since_epoch;
|
||||
};
|
||||
|
||||
static AsynchronouslyUpdatedTimestamps async_updated_timestamps;
|
||||
|
||||
void UpdateCoarseTimestamps() {
|
||||
void
|
||||
UpdateCoarseTimestamps()
|
||||
{
|
||||
async_updated_timestamps.steady_clock_time_since_epoch.store(
|
||||
ReadSteadyClock().time_since_epoch(),
|
||||
// std::chrono::steady_clock::now().time_since_epoch(),
|
||||
@ -66,13 +73,19 @@ void UpdateCoarseTimestamps() {
|
||||
// std::chrono::system_clock::now().time_since_epoch(),
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
constexpr std::chrono::microseconds CoarseClockInitializer::kAccuracy;
|
||||
CoarseClockInitializer *CoarseClockInitializer::Instance() {
|
||||
|
||||
CoarseClockInitializer *
|
||||
CoarseClockInitializer::Instance()
|
||||
{
|
||||
static NeverDestroyedSingleton<CoarseClockInitializer> cci;
|
||||
return cci.Get();
|
||||
}
|
||||
|
||||
void CoarseClockInitializer::Start() {
|
||||
void
|
||||
CoarseClockInitializer::Start()
|
||||
{
|
||||
if (!internal::TestAndSet(running_, false, true)) {
|
||||
TILE_LOG_WARNING("CoarseClockInitializer is already running");
|
||||
return;
|
||||
@ -81,11 +94,8 @@ void CoarseClockInitializer::Start() {
|
||||
UpdateCoarseTimestamps();
|
||||
|
||||
worker_ = make_unique<std::thread>([this] {
|
||||
const auto accuracy_as_ns =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(kAccuracy).count();
|
||||
TILE_CHECK_GE(accuracy_as_ns, 2000,
|
||||
"accuracy is too small, MUST GE 2000ns, current is {}",
|
||||
kAccuracy);
|
||||
const auto accuracy_as_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(kAccuracy).count();
|
||||
TILE_CHECK_GE(accuracy_as_ns, 2000, "accuracy is too small, MUST GE 2000ns, current is {}", kAccuracy);
|
||||
|
||||
while (running_.load(std::memory_order_relaxed)) {
|
||||
// std::this_thread::sleep_for(std::chrono::nanoseconds(500));
|
||||
@ -96,11 +106,15 @@ void CoarseClockInitializer::Start() {
|
||||
});
|
||||
}
|
||||
|
||||
void CoarseClockInitializer::Stop() {
|
||||
void
|
||||
CoarseClockInitializer::Stop()
|
||||
{
|
||||
running_.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void CoarseClockInitializer::Join() {
|
||||
void
|
||||
CoarseClockInitializer::Join()
|
||||
{
|
||||
if (worker_) {
|
||||
worker_->join();
|
||||
worker_.reset();
|
||||
@ -114,32 +128,36 @@ CoarseClockInitializer::~CoarseClockInitializer() {}
|
||||
}// namespace chrono
|
||||
}// namespace detail
|
||||
|
||||
std::chrono::steady_clock::time_point ReadSteadyClock() {
|
||||
std::chrono::steady_clock::time_point
|
||||
ReadSteadyClock()
|
||||
{
|
||||
// return ReadClock<std::chrono::steady_clock>(CLOCK_MONOTONIC);
|
||||
return std::chrono::steady_clock::now();
|
||||
}
|
||||
std::chrono::system_clock::time_point ReadSystemClock() {
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
ReadSystemClock()
|
||||
{
|
||||
// return ReadClock<std::chrono::system_clock>(CLOCK_REALTIME);
|
||||
return std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point ReadCoarseSteadyClock() {
|
||||
std::chrono::steady_clock::time_point
|
||||
ReadCoarseSteadyClock()
|
||||
{
|
||||
auto coarse_duration =
|
||||
detail::chrono::async_updated_timestamps.steady_clock_time_since_epoch
|
||||
.load(std::memory_order_relaxed);
|
||||
auto target_duration =
|
||||
std::chrono::duration_cast<std::chrono::steady_clock::duration>(
|
||||
coarse_duration);
|
||||
detail::chrono::async_updated_timestamps.steady_clock_time_since_epoch.load(std::memory_order_relaxed);
|
||||
auto target_duration = std::chrono::duration_cast<std::chrono::steady_clock::duration>(coarse_duration);
|
||||
return std::chrono::steady_clock::time_point(target_duration);
|
||||
}
|
||||
std::chrono::system_clock::time_point ReadCoarseSystemClock() {
|
||||
auto coarse_duration =
|
||||
detail::chrono::async_updated_timestamps.system_clock_time_since_epoch
|
||||
.load(std::memory_order_relaxed);
|
||||
|
||||
auto target_duration =
|
||||
std::chrono::duration_cast<std::chrono::system_clock::duration>(
|
||||
coarse_duration);
|
||||
std::chrono::system_clock::time_point
|
||||
ReadCoarseSystemClock()
|
||||
{
|
||||
auto coarse_duration =
|
||||
detail::chrono::async_updated_timestamps.system_clock_time_since_epoch.load(std::memory_order_relaxed);
|
||||
|
||||
auto target_duration = std::chrono::duration_cast<std::chrono::system_clock::duration>(coarse_duration);
|
||||
|
||||
return std::chrono::system_clock::time_point(target_duration);
|
||||
}
|
||||
|
@ -45,7 +45,10 @@ std::chrono::system_clock::time_point ReadSystemClock();
|
||||
std::chrono::steady_clock::time_point ReadCoarseSteadyClock();
|
||||
// accuracy: 10us
|
||||
std::chrono::system_clock::time_point ReadCoarseSystemClock();
|
||||
inline std::int64_t ReadUnixTimestamp() {
|
||||
|
||||
inline std::int64_t
|
||||
ReadUnixTimestamp()
|
||||
{
|
||||
return ReadCoarseSystemClock().time_since_epoch() / std::chrono::seconds(1);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,9 @@
|
||||
|
||||
namespace tile {
|
||||
|
||||
void Benchmark_GetTimeOfDay(benchmark::State &state) {
|
||||
void
|
||||
Benchmark_GetTimeOfDay(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
@ -17,39 +19,41 @@ void Benchmark_GetTimeOfDay(benchmark::State &state) {
|
||||
|
||||
BENCHMARK(Benchmark_GetTimeOfDay);
|
||||
|
||||
void Benchmark_StdSteadyClock(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
(void)std::chrono::steady_clock::now();
|
||||
}
|
||||
void
|
||||
Benchmark_StdSteadyClock(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) { (void) std::chrono::steady_clock::now(); }
|
||||
}
|
||||
|
||||
BENCHMARK(Benchmark_StdSteadyClock);
|
||||
|
||||
void Benchmark_StdSystemClock(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
(void)std::chrono::system_clock::now();
|
||||
}
|
||||
void
|
||||
Benchmark_StdSystemClock(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) { (void) std::chrono::system_clock::now(); }
|
||||
}
|
||||
|
||||
BENCHMARK(Benchmark_StdSystemClock);
|
||||
|
||||
void Benchmark_ReadSteadyClock(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
ReadSteadyClock();
|
||||
}
|
||||
void
|
||||
Benchmark_ReadSteadyClock(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) { ReadSteadyClock(); }
|
||||
}
|
||||
|
||||
BENCHMARK(Benchmark_ReadSteadyClock);
|
||||
|
||||
void Benchmark_ReadSystemClock(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
ReadSystemClock();
|
||||
}
|
||||
void
|
||||
Benchmark_ReadSystemClock(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) { ReadSystemClock(); }
|
||||
}
|
||||
|
||||
BENCHMARK(Benchmark_ReadSystemClock);
|
||||
|
||||
void Benchmark_ReadCoarseSteadyClock(benchmark::State &state) {
|
||||
void
|
||||
Benchmark_ReadCoarseSteadyClock(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) {
|
||||
// ... <+23>: lea 0x137c5a(%rip),%rax # `system_clock_time_since_epoch`
|
||||
// ... <+30>: mov (%rax),%rax
|
||||
@ -59,10 +63,10 @@ void Benchmark_ReadCoarseSteadyClock(benchmark::State &state) {
|
||||
|
||||
BENCHMARK(Benchmark_ReadCoarseSteadyClock);
|
||||
|
||||
void Benchmark_ReadCoarseSystemClock(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
ReadCoarseSystemClock();
|
||||
}
|
||||
void
|
||||
Benchmark_ReadCoarseSystemClock(benchmark::State &state)
|
||||
{
|
||||
while (state.KeepRunning()) { ReadCoarseSystemClock(); }
|
||||
}
|
||||
|
||||
BENCHMARK(Benchmark_ReadCoarseSystemClock);
|
||||
|
@ -7,7 +7,9 @@ namespace tile {
|
||||
static constexpr auto one_ms = std::chrono::milliseconds(1);
|
||||
static constexpr auto kTestN = 1000;
|
||||
|
||||
long AvageTime(std::function<long()> f, std::size_t n = kTestN) {
|
||||
long
|
||||
AvageTime(std::function<long()> f, std::size_t n = kTestN)
|
||||
{
|
||||
long double total = 0;
|
||||
for (std::size_t i = 0; i != n; ++i) {
|
||||
total += 1.0f / n * f();
|
||||
@ -16,33 +18,27 @@ long AvageTime(std::function<long()> f, std::size_t n = kTestN) {
|
||||
return static_cast<long>(total);
|
||||
}
|
||||
|
||||
TEST(SystemClock, Compare) {
|
||||
auto diff = AvageTime([] {
|
||||
return (ReadSystemClock() - std::chrono::system_clock::now()) / one_ms;
|
||||
});
|
||||
TEST(SystemClock, Compare)
|
||||
{
|
||||
auto diff = AvageTime([] { return (ReadSystemClock() - std::chrono::system_clock::now()) / one_ms; });
|
||||
ASSERT_NEAR(diff, 0, 5);
|
||||
}
|
||||
|
||||
TEST(SteadyClock, Compare) {
|
||||
auto diff = AvageTime([] {
|
||||
return (ReadSteadyClock() - std::chrono::steady_clock::now()) / one_ms;
|
||||
});
|
||||
TEST(SteadyClock, Compare)
|
||||
{
|
||||
auto diff = AvageTime([] { return (ReadSteadyClock() - std::chrono::steady_clock::now()) / one_ms; });
|
||||
ASSERT_NEAR(diff, 0, 5);
|
||||
}
|
||||
|
||||
TEST(CoarseSystemClock, Compare) {
|
||||
auto diff = AvageTime([] {
|
||||
return (ReadCoarseSystemClock() - std::chrono::system_clock::now()) /
|
||||
one_ms;
|
||||
});
|
||||
TEST(CoarseSystemClock, Compare)
|
||||
{
|
||||
auto diff = AvageTime([] { return (ReadCoarseSystemClock() - std::chrono::system_clock::now()) / one_ms; });
|
||||
ASSERT_NEAR(diff, 0, kTestN);
|
||||
}
|
||||
|
||||
TEST(CoarseSteadyClock, Compare) {
|
||||
auto diff = AvageTime([] {
|
||||
return (ReadCoarseSteadyClock() - std::chrono::steady_clock::now()) /
|
||||
one_ms;
|
||||
});
|
||||
TEST(CoarseSteadyClock, Compare)
|
||||
{
|
||||
auto diff = AvageTime([] { return (ReadCoarseSteadyClock() - std::chrono::steady_clock::now()) / one_ms; });
|
||||
ASSERT_NEAR(diff, 0, kTestN);
|
||||
}
|
||||
}// namespace tile
|
||||
|
@ -4,33 +4,37 @@
|
||||
|
||||
namespace tile {
|
||||
|
||||
std::unique_ptr<Decompressor> MakeDecompressor(Slice name) {
|
||||
std::unique_ptr<Decompressor>
|
||||
MakeDecompressor(Slice name)
|
||||
{
|
||||
return decompressor_registry.TryNew(name);
|
||||
}
|
||||
|
||||
std::unique_ptr<Compressor> MakeCompressor(Slice name) {
|
||||
std::unique_ptr<Compressor>
|
||||
MakeCompressor(Slice name)
|
||||
{
|
||||
return compressor_registry.TryNew(name);
|
||||
}
|
||||
|
||||
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor,
|
||||
const NoncontiguousBuffer &body) {
|
||||
std::optional<NoncontiguousBuffer>
|
||||
Compress(Compressor *compressor, const NoncontiguousBuffer &body)
|
||||
{
|
||||
NoncontiguousBufferBuilder builder;
|
||||
if (!Compress(compressor, body, &builder)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return builder.DestructiveGet();
|
||||
}
|
||||
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor,
|
||||
Slice body) {
|
||||
NoncontiguousBufferBuilder builder;
|
||||
if (!Compress(compressor, body, &builder)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!Compress(compressor, body, &builder)) { return std::nullopt; }
|
||||
return builder.DestructiveGet();
|
||||
}
|
||||
|
||||
bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb,
|
||||
NoncontiguousBufferBuilder *builder) {
|
||||
std::optional<NoncontiguousBuffer>
|
||||
Compress(Compressor *compressor, Slice body)
|
||||
{
|
||||
NoncontiguousBufferBuilder builder;
|
||||
if (!Compress(compressor, body, &builder)) { return std::nullopt; }
|
||||
return builder.DestructiveGet();
|
||||
}
|
||||
|
||||
bool
|
||||
Compress(Compressor *compressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder)
|
||||
{
|
||||
if (!compressor) {
|
||||
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
|
||||
return false;
|
||||
@ -40,8 +44,9 @@ bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb,
|
||||
return compressor->Compress(nb, &out);
|
||||
}
|
||||
|
||||
bool Compress(Compressor *compressor, Slice body,
|
||||
NoncontiguousBufferBuilder *builder) {
|
||||
bool
|
||||
Compress(Compressor *compressor, Slice body, NoncontiguousBufferBuilder *builder)
|
||||
{
|
||||
if (!compressor) {
|
||||
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
|
||||
return false;
|
||||
@ -51,25 +56,25 @@ bool Compress(Compressor *compressor, Slice body,
|
||||
return compressor->Compress(body.data(), body.size(), &out);
|
||||
}
|
||||
|
||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor,
|
||||
const NoncontiguousBuffer &body) {
|
||||
std::optional<NoncontiguousBuffer>
|
||||
Decompress(Decompressor *decompressor, const NoncontiguousBuffer &body)
|
||||
{
|
||||
NoncontiguousBufferBuilder builder;
|
||||
if (!Decompress(decompressor, body, &builder)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return builder.DestructiveGet();
|
||||
}
|
||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor,
|
||||
Slice body) {
|
||||
NoncontiguousBufferBuilder builder;
|
||||
if (!Decompress(decompressor, body, &builder)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!Decompress(decompressor, body, &builder)) { return std::nullopt; }
|
||||
return builder.DestructiveGet();
|
||||
}
|
||||
|
||||
bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb,
|
||||
NoncontiguousBufferBuilder *builder) {
|
||||
std::optional<NoncontiguousBuffer>
|
||||
Decompress(Decompressor *decompressor, Slice body)
|
||||
{
|
||||
NoncontiguousBufferBuilder builder;
|
||||
if (!Decompress(decompressor, body, &builder)) { return std::nullopt; }
|
||||
return builder.DestructiveGet();
|
||||
}
|
||||
|
||||
bool
|
||||
Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder)
|
||||
{
|
||||
if (!decompressor) {
|
||||
TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr");
|
||||
return false;
|
||||
@ -78,8 +83,10 @@ bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb,
|
||||
NoncontiguousBufferCompressionOutputStream out(builder);
|
||||
return decompressor->Decompress(nb, &out);
|
||||
}
|
||||
bool Decompress(Decompressor *decompressor, Slice body,
|
||||
NoncontiguousBufferBuilder *builder) {
|
||||
|
||||
bool
|
||||
Decompress(Decompressor *decompressor, Slice body, NoncontiguousBufferBuilder *builder)
|
||||
{
|
||||
if (!decompressor) {
|
||||
TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr");
|
||||
return false;
|
||||
|
@ -11,25 +11,18 @@ namespace tile {
|
||||
std::unique_ptr<Decompressor> MakeDecompressor(Slice name);
|
||||
std::unique_ptr<Compressor> MakeCompressor(Slice name);
|
||||
|
||||
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor,
|
||||
const NoncontiguousBuffer &nb);
|
||||
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor, const NoncontiguousBuffer &nb);
|
||||
std::optional<NoncontiguousBuffer> Compress(Compressor *compressor, Slice body);
|
||||
|
||||
bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb,
|
||||
NoncontiguousBufferBuilder *builder);
|
||||
bool Compress(Compressor *compressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder);
|
||||
|
||||
bool Compress(Compressor *compressor, Slice body,
|
||||
NoncontiguousBufferBuilder *builder);
|
||||
bool Compress(Compressor *compressor, Slice body, NoncontiguousBufferBuilder *builder);
|
||||
|
||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor,
|
||||
const NoncontiguousBuffer &body);
|
||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor,
|
||||
Slice body);
|
||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor, const NoncontiguousBuffer &body);
|
||||
std::optional<NoncontiguousBuffer> Decompress(Decompressor *decompressor, Slice body);
|
||||
|
||||
bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb,
|
||||
NoncontiguousBufferBuilder *builder);
|
||||
bool Decompress(Decompressor *decompressor, Slice body,
|
||||
NoncontiguousBufferBuilder *builder);
|
||||
bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder);
|
||||
bool Decompress(Decompressor *decompressor, Slice body, NoncontiguousBufferBuilder *builder);
|
||||
|
||||
}// namespace tile
|
||||
|
||||
|
@ -4,15 +4,15 @@ namespace tile {
|
||||
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(compressor_registry, Compressor);
|
||||
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(decompressor_registry, Decompressor);
|
||||
|
||||
TestCompressionOutputStream::TestCompressionOutputStream(std::string *s,
|
||||
std::size_t every_size)
|
||||
: buffer_(s), every_size_(every_size) {}
|
||||
TestCompressionOutputStream::TestCompressionOutputStream(std::string *s, std::size_t every_size)
|
||||
: buffer_(s),
|
||||
every_size_(every_size)
|
||||
{}
|
||||
|
||||
bool TestCompressionOutputStream::Next(void **data,
|
||||
std::size_t *size) noexcept {
|
||||
if (buffer_->size() < using_bytes_ + every_size_) {
|
||||
buffer_->resize(using_bytes_ + every_size_);
|
||||
}
|
||||
bool
|
||||
TestCompressionOutputStream::Next(void **data, std::size_t *size) noexcept
|
||||
{
|
||||
if (buffer_->size() < using_bytes_ + every_size_) { buffer_->resize(using_bytes_ + every_size_); }
|
||||
|
||||
*data = internal::RemoveConstPtr(buffer_->data()) + using_bytes_;
|
||||
*size = every_size_;
|
||||
@ -20,9 +20,17 @@ bool TestCompressionOutputStream::Next(void **data,
|
||||
using_bytes_ += every_size_;
|
||||
return true;
|
||||
}
|
||||
void TestCompressionOutputStream::BackUp(std::size_t count) noexcept {
|
||||
|
||||
void
|
||||
TestCompressionOutputStream::BackUp(std::size_t count) noexcept
|
||||
{
|
||||
using_bytes_ -= count;
|
||||
}
|
||||
void TestCompressionOutputStream::Flush() { buffer_->resize(using_bytes_); }
|
||||
|
||||
void
|
||||
TestCompressionOutputStream::Flush()
|
||||
{
|
||||
buffer_->resize(using_bytes_);
|
||||
}
|
||||
|
||||
}// namespace tile
|
||||
|
@ -18,19 +18,15 @@ public:
|
||||
class Compressor {
|
||||
public:
|
||||
virtual ~Compressor() = default;
|
||||
virtual bool Compress(const void *src, std::size_t size,
|
||||
CompressionOutputStream *out) = 0;
|
||||
virtual bool Compress(const NoncontiguousBuffer &src,
|
||||
CompressionOutputStream *out) = 0;
|
||||
virtual bool Compress(const void *src, std::size_t size, CompressionOutputStream *out) = 0;
|
||||
virtual bool Compress(const NoncontiguousBuffer &src, CompressionOutputStream *out) = 0;
|
||||
};
|
||||
|
||||
class Decompressor {
|
||||
public:
|
||||
~Decompressor() = default;
|
||||
virtual bool Decompress(const void *src, std::size_t size,
|
||||
CompressionOutputStream *out) = 0;
|
||||
virtual bool Decompress(const NoncontiguousBuffer &src,
|
||||
CompressionOutputStream *out) = 0;
|
||||
virtual bool Decompress(const void *src, std::size_t size, CompressionOutputStream *out) = 0;
|
||||
virtual bool Decompress(const NoncontiguousBuffer &src, CompressionOutputStream *out) = 0;
|
||||
};
|
||||
|
||||
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(compressor_registry, Compressor);
|
||||
@ -39,17 +35,14 @@ TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(decompressor_registry, Decompressor);
|
||||
}// namespace tile
|
||||
|
||||
#define TILE_COMPRESSION_REGISTER_COMPRESSOR(Name, Implementation) \
|
||||
TILE_REGISTER_CLASS_DEPENDENCY(::tile::compressor_registry, Name, \
|
||||
Implementation)
|
||||
TILE_REGISTER_CLASS_DEPENDENCY(::tile::compressor_registry, Name, Implementation)
|
||||
#define TILE_COMPRESSION_REGISTER_DECOMPRESSOR(Name, Implementation) \
|
||||
TILE_REGISTER_CLASS_DEPENDENCY(::tile::decompressor_registry, Name, \
|
||||
Implementation)
|
||||
TILE_REGISTER_CLASS_DEPENDENCY(::tile::decompressor_registry, Name, Implementation)
|
||||
|
||||
namespace tile {
|
||||
class TestCompressionOutputStream : public CompressionOutputStream {
|
||||
public:
|
||||
explicit TestCompressionOutputStream(std::string *s,
|
||||
std::size_t every_size = 2);
|
||||
explicit TestCompressionOutputStream(std::string *s, std::size_t every_size = 2);
|
||||
|
||||
bool Next(void **data, std::size_t *size) noexcept override;
|
||||
void BackUp(std::size_t count) noexcept override;
|
||||
|
@ -14,10 +14,12 @@ struct ZStream {
|
||||
z_stream stream;
|
||||
};
|
||||
}// namespace detail
|
||||
|
||||
namespace {
|
||||
|
||||
inline double EstimateCompressionRate(const z_stream *stream,
|
||||
double default_value) {
|
||||
inline double
|
||||
EstimateCompressionRate(const z_stream *stream, double default_value)
|
||||
{
|
||||
if (stream->total_in > 0) {
|
||||
double rate = 1.0f * stream->total_out / stream->total_in;
|
||||
constexpr double kMinRate = 1.1;
|
||||
@ -27,7 +29,9 @@ inline double EstimateCompressionRate(const z_stream *stream,
|
||||
return default_value;
|
||||
}
|
||||
|
||||
inline uint32_t RestrictAvailSize(size_t size) {
|
||||
inline uint32_t
|
||||
RestrictAvailSize(size_t size)
|
||||
{
|
||||
return static_cast<uint32_t>(std::min(size, static_cast<size_t>(UINT32_MAX)));
|
||||
}
|
||||
|
||||
@ -37,9 +41,14 @@ constexpr int ZLIB_INIT_FLAG_GZIP = 16;
|
||||
// he size to increase every time Z_BUF_ERROR returns.
|
||||
constexpr int kOutBufferIncreaseSize = 32;
|
||||
|
||||
bool DoAppend(z_stream *stream, CompressionOutputStream *out_stream,
|
||||
const void *input_buffer, std::size_t input_size, bool is_deflate,
|
||||
bool finish) {
|
||||
bool
|
||||
DoAppend(z_stream *stream,
|
||||
CompressionOutputStream *out_stream,
|
||||
const void *input_buffer,
|
||||
std::size_t input_size,
|
||||
bool is_deflate,
|
||||
bool finish)
|
||||
{
|
||||
TILE_CHECK(stream && out_stream);
|
||||
|
||||
if (!finish && (input_buffer == nullptr || input_size == 0)) {
|
||||
@ -67,18 +76,15 @@ bool DoAppend(z_stream *stream, CompressionOutputStream *out_stream,
|
||||
stream->next_out = reinterpret_cast<Bytef *>(out_data);
|
||||
} else {
|
||||
double rate = EstimateCompressionRate(stream, 0.5);
|
||||
tmp_buffer.resize(left_size * rate +
|
||||
need_more_space_cnt * kOutBufferIncreaseSize);
|
||||
stream->next_out = reinterpret_cast<Bytef *>(
|
||||
internal::RemoveConstPtr(tmp_buffer.data()));
|
||||
tmp_buffer.resize(left_size * rate + need_more_space_cnt * kOutBufferIncreaseSize);
|
||||
stream->next_out = reinterpret_cast<Bytef *>(internal::RemoveConstPtr(tmp_buffer.data()));
|
||||
out_size = tmp_buffer.size();
|
||||
}
|
||||
|
||||
stream->avail_out = RestrictAvailSize(out_size);
|
||||
const std::size_t current_avail_out = stream->avail_out;
|
||||
|
||||
TILE_CHECK_GT(stream->avail_out, 0,
|
||||
"Avail_out should never be zero before the call.");
|
||||
TILE_CHECK_GT(stream->avail_out, 0, "Avail_out should never be zero before the call.");
|
||||
int flush_option = finish ? Z_FINISH : Z_NO_FLUSH;
|
||||
if (is_deflate) {
|
||||
code = deflate(stream, flush_option);
|
||||
@ -89,9 +95,7 @@ bool DoAppend(z_stream *stream, CompressionOutputStream *out_stream,
|
||||
// https://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/zlib-inflate-1.html
|
||||
// No progress is possible; either avail_in or avail_out was zero.
|
||||
if (code == Z_BUF_ERROR) {
|
||||
if (need_more_space_cnt == 0) {
|
||||
out_stream->BackUp(out_size);
|
||||
}
|
||||
if (need_more_space_cnt == 0) { out_stream->BackUp(out_size); }
|
||||
|
||||
if (stream->avail_in == 0) {
|
||||
// if not finish, we need more input data
|
||||
@ -115,32 +119,32 @@ bool DoAppend(z_stream *stream, CompressionOutputStream *out_stream,
|
||||
out_stream->BackUp(stream->avail_out);
|
||||
} else {
|
||||
if (TILE_UNLIKELY(!CopyDataToCompressionOutputStream(
|
||||
out_stream, tmp_buffer.data(),
|
||||
current_avail_out - stream->avail_out))) {
|
||||
out_stream, tmp_buffer.data(), current_avail_out - stream->avail_out))) {
|
||||
return false;
|
||||
}
|
||||
need_more_space_cnt = 0;
|
||||
}
|
||||
|
||||
if (code == Z_STREAM_END) {
|
||||
return true;
|
||||
}
|
||||
if (code == Z_STREAM_END) { return true; }
|
||||
}
|
||||
|
||||
return code == Z_OK;
|
||||
}
|
||||
}// namespace
|
||||
|
||||
bool GzipCompressor::Compress(const void *src, std::size_t size,
|
||||
CompressionOutputStream *out) {
|
||||
bool
|
||||
GzipCompressor::Compress(const void *src, std::size_t size, CompressionOutputStream *out)
|
||||
{
|
||||
bool ok = Init(out);
|
||||
ok &= Append(src, size);
|
||||
ok &= Flush();
|
||||
out_ = nullptr;
|
||||
return ok;
|
||||
}
|
||||
bool GzipCompressor::Compress(const NoncontiguousBuffer &bytes,
|
||||
CompressionOutputStream *out) {
|
||||
|
||||
bool
|
||||
GzipCompressor::Compress(const NoncontiguousBuffer &bytes, CompressionOutputStream *out)
|
||||
{
|
||||
bool ok = Init(out);
|
||||
std::size_t left = bytes.ByteSize();
|
||||
for (auto iter = bytes.begin(); ok && iter != bytes.end(); ++iter) {
|
||||
@ -152,23 +156,28 @@ bool GzipCompressor::Compress(const NoncontiguousBuffer &bytes,
|
||||
out_ = nullptr;
|
||||
return ok;
|
||||
}
|
||||
bool GzipCompressor::Append(const void *buffer, std::size_t size) {
|
||||
|
||||
bool
|
||||
GzipCompressor::Append(const void *buffer, std::size_t size)
|
||||
{
|
||||
return DoAppend(&stream_->stream, out_, buffer, size, true, false);
|
||||
}
|
||||
bool GzipCompressor::Flush() {
|
||||
|
||||
bool
|
||||
GzipCompressor::Flush()
|
||||
{
|
||||
TILE_CHECK(out_);
|
||||
if (!DoAppend(&stream_->stream, out_, nullptr, 0, true, true)) {
|
||||
return false;
|
||||
}
|
||||
if (!DoAppend(&stream_->stream, out_, nullptr, 0, true, true)) { return false; }
|
||||
return Release();
|
||||
}
|
||||
|
||||
bool GzipCompressor::Init(CompressionOutputStream *out) {
|
||||
bool
|
||||
GzipCompressor::Init(CompressionOutputStream *out)
|
||||
{
|
||||
TILE_CHECK(!out_);
|
||||
stream_ = make_unique<detail::ZStream>();
|
||||
int code =
|
||||
deflateInit2(&stream_->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||
MAX_WBITS + ZLIB_INIT_FLAG_GZIP, 8, Z_DEFAULT_STRATEGY);
|
||||
int code = deflateInit2(
|
||||
&stream_->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + ZLIB_INIT_FLAG_GZIP, 8, Z_DEFAULT_STRATEGY);
|
||||
if (code != Z_OK) {
|
||||
TILE_LOG_ERROR_EVERY_SECOND("deflateInit2 error code [{}]", code);
|
||||
stream_ = nullptr;
|
||||
@ -177,25 +186,29 @@ bool GzipCompressor::Init(CompressionOutputStream *out) {
|
||||
}
|
||||
return code == Z_OK;
|
||||
}
|
||||
bool GzipCompressor::Release() {
|
||||
|
||||
bool
|
||||
GzipCompressor::Release()
|
||||
{
|
||||
TILE_CHECK(stream_);
|
||||
int code = deflateEnd(&stream_->stream);
|
||||
if (code != Z_OK) {
|
||||
TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code);
|
||||
}
|
||||
if (code != Z_OK) { TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code); }
|
||||
return code == Z_OK;
|
||||
}
|
||||
|
||||
bool GzipDecompressor::Decompress(const void *src, std::size_t size,
|
||||
CompressionOutputStream *out) {
|
||||
bool
|
||||
GzipDecompressor::Decompress(const void *src, std::size_t size, CompressionOutputStream *out)
|
||||
{
|
||||
bool ok = Init(out);
|
||||
ok &= Append(src, size);
|
||||
ok &= Flush();
|
||||
out_ = nullptr;
|
||||
return ok;
|
||||
}
|
||||
bool GzipDecompressor::Decompress(const NoncontiguousBuffer &compressed,
|
||||
CompressionOutputStream *out) {
|
||||
|
||||
bool
|
||||
GzipDecompressor::Decompress(const NoncontiguousBuffer &compressed, CompressionOutputStream *out)
|
||||
{
|
||||
bool ok = Init(out);
|
||||
std::size_t left = compressed.ByteSize();
|
||||
for (auto iter = compressed.begin(); ok && iter != compressed.end(); ++iter) {
|
||||
@ -208,17 +221,23 @@ bool GzipDecompressor::Decompress(const NoncontiguousBuffer &compressed,
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool GzipDecompressor::Append(const void *buffer, std::size_t size) {
|
||||
bool
|
||||
GzipDecompressor::Append(const void *buffer, std::size_t size)
|
||||
{
|
||||
return DoAppend(&stream_->stream, out_, buffer, size, false, false);
|
||||
}
|
||||
bool GzipDecompressor::Flush() {
|
||||
|
||||
bool
|
||||
GzipDecompressor::Flush()
|
||||
{
|
||||
TILE_CHECK(out_);
|
||||
if (!DoAppend(&stream_->stream, out_, nullptr, 0, false, true)) {
|
||||
return false;
|
||||
}
|
||||
if (!DoAppend(&stream_->stream, out_, nullptr, 0, false, true)) { return false; }
|
||||
return Release();
|
||||
}
|
||||
bool GzipDecompressor::Init(CompressionOutputStream *out) {
|
||||
|
||||
bool
|
||||
GzipDecompressor::Init(CompressionOutputStream *out)
|
||||
{
|
||||
TILE_CHECK(!out_);
|
||||
stream_ = make_unique<detail::ZStream>();
|
||||
int code = inflateInit2(&stream_->stream, MAX_WBITS + ZLIB_INIT_FLAG_GZIP);
|
||||
@ -230,12 +249,13 @@ bool GzipDecompressor::Init(CompressionOutputStream *out) {
|
||||
}
|
||||
return code == Z_OK;
|
||||
}
|
||||
bool GzipDecompressor::Release() {
|
||||
|
||||
bool
|
||||
GzipDecompressor::Release()
|
||||
{
|
||||
TILE_CHECK(stream_);
|
||||
int code = inflateEnd(&stream_->stream);
|
||||
if (code != Z_OK) {
|
||||
TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code);
|
||||
}
|
||||
if (code != Z_OK) { TILE_LOG_WARNING_EVERY_SECOND("DeflateEnd fail with code [{}].", code); }
|
||||
return code == Z_OK;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
#include "tile/base/compression/compression.h"
|
||||
|
||||
namespace tile {
|
||||
@ -16,10 +15,8 @@ struct ZStream;
|
||||
|
||||
class GzipCompressor : public Compressor {
|
||||
public:
|
||||
bool Compress(const void *src, std::size_t size,
|
||||
CompressionOutputStream *out) override;
|
||||
bool Compress(const NoncontiguousBuffer &bytes,
|
||||
CompressionOutputStream *out) override;
|
||||
bool Compress(const void *src, std::size_t size, CompressionOutputStream *out) override;
|
||||
bool Compress(const NoncontiguousBuffer &bytes, CompressionOutputStream *out) override;
|
||||
|
||||
private:
|
||||
bool Append(const void *buffer, std::size_t size);
|
||||
@ -32,10 +29,8 @@ private:
|
||||
|
||||
class GzipDecompressor : public Decompressor {
|
||||
public:
|
||||
bool Decompress(const void *src, std::size_t size,
|
||||
CompressionOutputStream *out) override;
|
||||
bool Decompress(const NoncontiguousBuffer &compressed,
|
||||
CompressionOutputStream *out) override;
|
||||
bool Decompress(const void *src, std::size_t size, CompressionOutputStream *out) override;
|
||||
bool Decompress(const NoncontiguousBuffer &compressed, CompressionOutputStream *out) override;
|
||||
|
||||
private:
|
||||
bool Append(const void *buffer, std::size_t size);
|
||||
|
@ -1,12 +1,12 @@
|
||||
#include "util.h"
|
||||
|
||||
namespace tile {
|
||||
namespace compression {
|
||||
bool CopyDataToCompressionOutputStream(CompressionOutputStream *out,
|
||||
const void *data, std::size_t size) {
|
||||
bool
|
||||
CopyDataToCompressionOutputStream(CompressionOutputStream *out, const void *data, std::size_t size)
|
||||
{
|
||||
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
if (size == 0) { return true; }
|
||||
|
||||
std::size_t current_pos = 0;
|
||||
std::size_t left_to_copy = size;
|
||||
@ -14,18 +14,14 @@ bool CopyDataToCompressionOutputStream(CompressionOutputStream *out,
|
||||
while (true) {
|
||||
void *next_data;
|
||||
std::size_t next_size;
|
||||
if (TILE_UNLIKELY(!out->Next(&next_data, &next_size))) {
|
||||
return false;
|
||||
}
|
||||
if (TILE_UNLIKELY(!out->Next(&next_data, &next_size))) { return false; }
|
||||
|
||||
if (left_to_copy <= next_size) {
|
||||
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos,
|
||||
left_to_copy);
|
||||
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos, left_to_copy);
|
||||
out->BackUp(next_size - left_to_copy);
|
||||
return true;
|
||||
} else {
|
||||
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos,
|
||||
next_size);
|
||||
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos, next_size);
|
||||
current_pos += next_size;
|
||||
left_to_copy -= next_size;
|
||||
}
|
||||
|
@ -7,8 +7,7 @@
|
||||
|
||||
namespace tile {
|
||||
namespace compression {
|
||||
bool CopyDataToCompressionOutputStream(CompressionOutputStream *out,
|
||||
const void *data, std::size_t size);
|
||||
bool CopyDataToCompressionOutputStream(CompressionOutputStream *out, const void *data, std::size_t size);
|
||||
}
|
||||
}// namespace tile
|
||||
|
||||
|
@ -4,7 +4,8 @@
|
||||
|
||||
namespace tile {
|
||||
namespace compression {
|
||||
TEST(CopyDataToCompressionOutputStream, All) {
|
||||
TEST(CopyDataToCompressionOutputStream, All)
|
||||
{
|
||||
std::string s;
|
||||
std::string a = "123456789+";
|
||||
|
||||
|
@ -11,21 +11,25 @@ static const char *algos[] = {
|
||||
"gzip",
|
||||
};
|
||||
}
|
||||
TEST(MakeCompressor, All) {
|
||||
|
||||
TEST(MakeCompressor, All)
|
||||
{
|
||||
auto &&c = MakeCompressor("gzip");
|
||||
EXPECT_TRUE(c);
|
||||
c = MakeCompressor("??");
|
||||
EXPECT_FALSE(c);
|
||||
}
|
||||
|
||||
TEST(MakeDecompressor, All) {
|
||||
TEST(MakeDecompressor, All)
|
||||
{
|
||||
auto &&c = MakeDecompressor("gzip");
|
||||
EXPECT_TRUE(c);
|
||||
c = MakeDecompressor("??");
|
||||
EXPECT_FALSE(c);
|
||||
}
|
||||
|
||||
TEST(CompressString, All) {
|
||||
TEST(CompressString, All)
|
||||
{
|
||||
std::string original(1000, 'A');
|
||||
auto c = Compress(MakeCompressor("gzip").get(), original);
|
||||
EXPECT_TRUE(c);
|
||||
@ -34,7 +38,8 @@ TEST(CompressString, All) {
|
||||
EXPECT_EQ(FlattenSlow(*d), original);
|
||||
}
|
||||
|
||||
TEST(CompressNoncontiguousBuffer, All) {
|
||||
TEST(CompressNoncontiguousBuffer, All)
|
||||
{
|
||||
NoncontiguousBufferBuilder nbb;
|
||||
std::string original(1000, 'A');
|
||||
nbb.Append(original.data(), original.size());
|
||||
@ -46,7 +51,8 @@ TEST(CompressNoncontiguousBuffer, All) {
|
||||
EXPECT_EQ(FlattenSlow(*d), original);
|
||||
}
|
||||
|
||||
TEST(Decompressor, Empty) {
|
||||
TEST(Decompressor, Empty)
|
||||
{
|
||||
for (auto &&algo : algos) {
|
||||
auto res = Decompress(MakeDecompressor(algo).get(), "");
|
||||
|
||||
@ -61,11 +67,10 @@ TEST(Decompressor, Empty) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Decompressor, Invalid) {
|
||||
TEST(Decompressor, Invalid)
|
||||
{
|
||||
for (auto &&algo : algos) {
|
||||
auto res =
|
||||
Decompress(MakeDecompressor(algo).get(),
|
||||
"this buffer is likely an invalid compressed buffer.");
|
||||
auto res = Decompress(MakeDecompressor(algo).get(), "this buffer is likely an invalid compressed buffer.");
|
||||
EXPECT_FALSE(res);
|
||||
}
|
||||
}
|
||||
|
@ -4,33 +4,45 @@
|
||||
#include "tile/base/thread/unique_lock.h"
|
||||
|
||||
namespace tile {
|
||||
bool Configuration::Has(const Slice &key) const {
|
||||
bool
|
||||
Configuration::Has(const Slice &key) const
|
||||
{
|
||||
UniqueLock<tile::Mutex> lock(mutex_);
|
||||
std::string value;
|
||||
return GetRaw(key, &value);
|
||||
}
|
||||
|
||||
void Configuration::Remove(const Slice &key) {
|
||||
void
|
||||
Configuration::Remove(const Slice &key)
|
||||
{
|
||||
UniqueLock<tile::Mutex> lock(mutex_);
|
||||
if (events_enabled_) {
|
||||
OnRemoving(key);
|
||||
}
|
||||
if (events_enabled_) { OnRemoving(key); }
|
||||
RemoveRaw(key);
|
||||
if (events_enabled_) {
|
||||
OnRemoved(key);
|
||||
}
|
||||
if (events_enabled_) { OnRemoved(key); }
|
||||
}
|
||||
|
||||
void Configuration::EnableEvents(bool enable) { events_enabled_ = enable; }
|
||||
bool Configuration::EventsEnabled() const { return events_enabled_; }
|
||||
void
|
||||
Configuration::EnableEvents(bool enable)
|
||||
{
|
||||
events_enabled_ = enable;
|
||||
}
|
||||
|
||||
Configuration::Keys Configuration::keys(const Slice &root) const {
|
||||
bool
|
||||
Configuration::EventsEnabled() const
|
||||
{
|
||||
return events_enabled_;
|
||||
}
|
||||
|
||||
Configuration::Keys
|
||||
Configuration::keys(const Slice &root) const
|
||||
{
|
||||
UniqueLock<tile::Mutex> lock(mutex_);
|
||||
return Enumerate(root);
|
||||
}
|
||||
|
||||
#define TILE_DEFINE_CONFIG_GETTER(type, name) \
|
||||
std::optional<type> Configuration::Get##name(const Slice &key) const { \
|
||||
std::optional<type> Configuration::Get##name(const Slice &key) const \
|
||||
{ \
|
||||
std::string value; \
|
||||
if (GetRaw(key, &value)) { \
|
||||
auto opt = TryParseTraits<type>::TryParse(value); \
|
||||
@ -43,7 +55,8 @@ Configuration::Keys Configuration::keys(const Slice &root) const {
|
||||
return std::nullopt; \
|
||||
} \
|
||||
} \
|
||||
type Configuration::Get##name(const Slice &key, type default_value) const { \
|
||||
type Configuration::Get##name(const Slice &key, type default_value) const \
|
||||
{ \
|
||||
auto opt = Get##name(key); \
|
||||
if (opt.has_value()) { \
|
||||
return *opt; \
|
||||
@ -53,11 +66,11 @@ Configuration::Keys Configuration::keys(const Slice &root) const {
|
||||
}
|
||||
|
||||
#define TILE_DEFINE_CONFIG_SETTER(type, name) \
|
||||
void Configuration::Set##name(const Slice &key, type value) { \
|
||||
SetRawWithEvent(key, Format("{}", value)); \
|
||||
}
|
||||
void Configuration::Set##name(const Slice &key, type value) { SetRawWithEvent(key, Format("{}", value)); }
|
||||
|
||||
std::optional<std::string> Configuration::GetString(const Slice &key) const {
|
||||
std::optional<std::string>
|
||||
Configuration::GetString(const Slice &key) const
|
||||
{
|
||||
std::string value;
|
||||
UniqueLock<tile::Mutex> lock(mutex_);
|
||||
if (GetRaw(key, &value)) {
|
||||
@ -67,16 +80,22 @@ std::optional<std::string> Configuration::GetString(const Slice &key) const {
|
||||
}
|
||||
}
|
||||
|
||||
std::string Configuration::GetString(const Slice &key,
|
||||
std::string default_value) const {
|
||||
std::string
|
||||
Configuration::GetString(const Slice &key, std::string default_value) const
|
||||
{
|
||||
auto opt = GetString(key);
|
||||
return opt.has_value() ? *opt : default_value;
|
||||
}
|
||||
|
||||
void Configuration::SetString(const Slice &key, const std::string &value) {
|
||||
void
|
||||
Configuration::SetString(const Slice &key, const std::string &value)
|
||||
{
|
||||
SetRawWithEvent(key, value);
|
||||
}
|
||||
void Configuration::SetBool(const Slice &key, bool value) {
|
||||
|
||||
void
|
||||
Configuration::SetBool(const Slice &key, bool value)
|
||||
{
|
||||
SetRawWithEvent(key, value ? "true" : "false");
|
||||
}
|
||||
|
||||
@ -101,20 +120,17 @@ TILE_DEFINE_CONFIG_SETTER(uint32_t, UInt32)
|
||||
TILE_DEFINE_CONFIG_SETTER(uint64_t, UInt64)
|
||||
TILE_DEFINE_CONFIG_SETTER(double, Double)
|
||||
|
||||
void Configuration::SetRawWithEvent(const Slice &key,
|
||||
const std::string &value) {
|
||||
if (events_enabled_) {
|
||||
OnChanging(key, value);
|
||||
}
|
||||
void
|
||||
Configuration::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);
|
||||
}
|
||||
if (events_enabled_) { OnChanged(key, value); }
|
||||
}
|
||||
|
||||
Configuration::~Configuration() {}
|
||||
|
@ -36,8 +36,7 @@ public:
|
||||
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);
|
||||
#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()
|
||||
@ -96,9 +95,8 @@ protected:
|
||||
protected:
|
||||
class ScopedLock {
|
||||
public:
|
||||
explicit ScopedLock(const Configuration &config) : config_(config) {
|
||||
config_.mutex_.Lock();
|
||||
}
|
||||
explicit ScopedLock(const Configuration &config) : config_(config) { config_.mutex_.Lock(); }
|
||||
|
||||
~ScopedLock() { config_.mutex_.Unlock(); }
|
||||
|
||||
private:
|
||||
|
@ -1,31 +1,38 @@
|
||||
#include "tile/base/config/ini_file_configuration.h"
|
||||
|
||||
#include "tile/base/string.h"
|
||||
#include "tile/base/thread/scoped_lock.h"
|
||||
|
||||
namespace tile {
|
||||
|
||||
IniFileConfiguration::IniFileConfiguration() {}
|
||||
|
||||
// IniFileConfig::IniFileConfig(std::istream &istr) { load(istr); }
|
||||
// IniFileConfig::IniFileConfig(const std::string &path) { load(path); }
|
||||
IniFileConfiguration::~IniFileConfiguration() {}
|
||||
|
||||
bool IniFileConfiguration::load(std::istream &istr) {
|
||||
if (!istr.good()) {
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
IniFileConfiguration::load(std::istream &istr)
|
||||
{
|
||||
if (!istr.good()) { return false; }
|
||||
|
||||
Configuration::ScopedLock lock(*this);
|
||||
map_.clear();
|
||||
section_key_.clear();
|
||||
while (!istr.eof()) {
|
||||
ParseLine(istr);
|
||||
}
|
||||
while (!istr.eof()) { ParseLine(istr); }
|
||||
return true;
|
||||
}
|
||||
bool IniFileConfiguration::load(const std::string &path) {
|
||||
|
||||
bool
|
||||
IniFileConfiguration::load(const std::string &path)
|
||||
{
|
||||
std::ifstream istr(path);
|
||||
return load(istr);
|
||||
}
|
||||
bool IniFileConfiguration::GetRaw(const Slice &key, std::string *value) const {
|
||||
|
||||
bool
|
||||
IniFileConfiguration::GetRaw(const Slice &key, std::string *value) const
|
||||
{
|
||||
auto iter = map_.find(key.ToString());
|
||||
if (iter != map_.end()) {
|
||||
*value = iter->second;
|
||||
@ -34,21 +41,23 @@ bool IniFileConfiguration::GetRaw(const Slice &key, std::string *value) const {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool IniFileConfiguration::SetRaw(const Slice &key, const Slice &value) {
|
||||
|
||||
bool
|
||||
IniFileConfiguration::SetRaw(const Slice &key, const Slice &value)
|
||||
{
|
||||
map_[key] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
void IniFileConfiguration::RemoveRaw(const Slice &key) {
|
||||
void
|
||||
IniFileConfiguration::RemoveRaw(const Slice &key)
|
||||
{
|
||||
std::string prefix = key;
|
||||
if (!prefix.empty()) {
|
||||
prefix.push_back('.');
|
||||
}
|
||||
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)) {
|
||||
if (EqualsIgnoreCase(cur->first, key) || EqualsIgnoreCase(cur->first, prefix)) {
|
||||
it = map_.erase(cur);
|
||||
} else {
|
||||
++it;
|
||||
@ -56,13 +65,13 @@ void IniFileConfiguration::RemoveRaw(const Slice &key) {
|
||||
}
|
||||
}
|
||||
|
||||
Configuration::Keys IniFileConfiguration::Enumerate(const Slice &key) const {
|
||||
Configuration::Keys
|
||||
IniFileConfiguration::Enumerate(const Slice &key) const
|
||||
{
|
||||
Configuration::Keys range;
|
||||
std::set<Slice> keys;
|
||||
std::string prefix = key.ToString();
|
||||
if (prefix.empty()) {
|
||||
prefix.push_back('.');
|
||||
}
|
||||
if (prefix.empty()) { prefix.push_back('.'); }
|
||||
|
||||
std::string::size_type psize = prefix.size();
|
||||
for (const auto &p : map_) {
|
||||
@ -85,7 +94,9 @@ Configuration::Keys IniFileConfiguration::Enumerate(const Slice &key) const {
|
||||
return range;
|
||||
}
|
||||
|
||||
void IniFileConfiguration::ParseLine(std::istream &istr) {
|
||||
void
|
||||
IniFileConfiguration::ParseLine(std::istream &istr)
|
||||
{
|
||||
static const int eof = std::char_traits<char>::eof();
|
||||
auto ReadLine = [&](std::string *line) {
|
||||
line->clear();
|
||||
@ -101,9 +112,7 @@ void IniFileConfiguration::ParseLine(std::istream &istr) {
|
||||
|
||||
std::string raw_line;
|
||||
while (ReadLine(&raw_line)) {
|
||||
if (raw_line.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (raw_line.empty()) { continue; }
|
||||
Slice line = TrimLeft(raw_line);
|
||||
if (line.empty() || line[0] == ';' || line[0] == '#') {
|
||||
// skip empty line
|
||||
@ -122,9 +131,7 @@ void IniFileConfiguration::ParseLine(std::istream &istr) {
|
||||
} else {
|
||||
auto strs = Split(line, "=", true, 2);
|
||||
std::string full_key = section_key_;
|
||||
if (!full_key.empty()) {
|
||||
full_key.push_back('.');
|
||||
}
|
||||
if (!full_key.empty()) { full_key.push_back('.'); }
|
||||
full_key.append(Trim(strs[0]));
|
||||
if (strs.size() > 1) {
|
||||
map_[full_key] = Trim(strs[1]);
|
||||
@ -134,8 +141,10 @@ void IniFileConfiguration::ParseLine(std::istream &istr) {
|
||||
}
|
||||
}
|
||||
}
|
||||
bool IniFileConfiguration::ICompare::operator()(const std::string &s1,
|
||||
const std::string &s2) const {
|
||||
|
||||
bool
|
||||
IniFileConfiguration::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;
|
||||
}
|
||||
|
@ -26,9 +26,11 @@ protected:
|
||||
|
||||
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_;
|
||||
|
@ -17,10 +17,10 @@ a=4
|
||||
|
||||
namespace tile {
|
||||
|
||||
static_assert(
|
||||
!detail::HasClassofImpl<IniFileConfiguration, Configuration>::value, "");
|
||||
static_assert(!detail::HasClassofImpl<IniFileConfiguration, Configuration>::value, "");
|
||||
|
||||
TEST(IniFileConfig, LoadFromIStream) {
|
||||
TEST(IniFileConfig, LoadFromIStream)
|
||||
{
|
||||
std::stringstream ss(kIniFileConfig);
|
||||
Configuration::Ptr config = MakeRefCounted<IniFileConfiguration>();
|
||||
ASSERT_FALSE(config->Has("a"));
|
||||
|
@ -1,19 +1,24 @@
|
||||
#include "tile/base/config/json_configuration.h"
|
||||
|
||||
#include "tile/base/logging.h"
|
||||
#include "tile/base/string.h"
|
||||
|
||||
namespace tile {
|
||||
JSONConfiguration::JSONConfiguration() : object_() {}
|
||||
|
||||
JSONConfiguration::~JSONConfiguration() {}
|
||||
|
||||
bool JSONConfiguration::load(const std::string &path) {
|
||||
bool
|
||||
JSONConfiguration::load(const std::string &path)
|
||||
{
|
||||
std::ifstream istr(path);
|
||||
return load(istr);
|
||||
}
|
||||
|
||||
bool JSONConfiguration::load(std::istream &istr) {
|
||||
if (!istr.good()) {
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
JSONConfiguration::load(std::istream &istr)
|
||||
{
|
||||
if (!istr.good()) { return false; }
|
||||
|
||||
Configuration::ScopedLock _(*this);
|
||||
object_.clear();
|
||||
@ -21,40 +26,53 @@ bool JSONConfiguration::load(std::istream &istr) {
|
||||
return reader.parse(istr, object_, true);
|
||||
}
|
||||
|
||||
void JSONConfiguration::SetInt(const Slice &key, int value) {
|
||||
void
|
||||
JSONConfiguration::SetInt(const Slice &key, int value)
|
||||
{
|
||||
Configuration::ScopedLock _(*this);
|
||||
SetValue(key, value);
|
||||
}
|
||||
|
||||
void JSONConfiguration::SetBool(const Slice &key, bool value) {
|
||||
void
|
||||
JSONConfiguration::SetBool(const Slice &key, bool value)
|
||||
{
|
||||
Configuration::ScopedLock _(*this);
|
||||
SetValue(key, value);
|
||||
}
|
||||
|
||||
void JSONConfiguration::SetDouble(const Slice &key, double value) {
|
||||
void
|
||||
JSONConfiguration::SetDouble(const Slice &key, double value)
|
||||
{
|
||||
Configuration::ScopedLock _(*this);
|
||||
SetValue(key, value);
|
||||
}
|
||||
|
||||
void JSONConfiguration::SetString(const Slice &key, const std::string &value) {
|
||||
void
|
||||
JSONConfiguration::SetString(const Slice &key, const std::string &value)
|
||||
{
|
||||
Configuration::ScopedLock _(*this);
|
||||
SetValue(key, value);
|
||||
}
|
||||
|
||||
void JSONConfiguration::RemoveRaw(const Slice &key) {
|
||||
void
|
||||
JSONConfiguration::RemoveRaw(const Slice &key)
|
||||
{
|
||||
Json::Value *root;
|
||||
Slice last_part;
|
||||
if (!FindStart(key, &last_part, &root)) {
|
||||
return;
|
||||
}
|
||||
if (!FindStart(key, &last_part, &root)) { return; }
|
||||
root->removeMember(last_part);
|
||||
}
|
||||
std::string JSONConfiguration::Dump() const {
|
||||
|
||||
std::string
|
||||
JSONConfiguration::Dump() const
|
||||
{
|
||||
Configuration::ScopedLock _(*this);
|
||||
return object_.toStyledString();
|
||||
}
|
||||
|
||||
bool JSONConfiguration::GetRaw(const Slice &key, std::string *value) const {
|
||||
bool
|
||||
JSONConfiguration::GetRaw(const Slice &key, std::string *value) const
|
||||
{
|
||||
auto keys = Split(key, '.');
|
||||
auto root = object_;
|
||||
for (std::size_t i = 0; i < keys.size(); ++i) {
|
||||
@ -63,9 +81,7 @@ bool JSONConfiguration::GetRaw(const Slice &key, std::string *value) const {
|
||||
TILE_LOG_ERROR("Invalid key: {}", key);
|
||||
return false;
|
||||
}
|
||||
if (!root.isMember(cur_key)) {
|
||||
return false;
|
||||
}
|
||||
if (!root.isMember(cur_key)) { return false; }
|
||||
root = root[cur_key];
|
||||
}
|
||||
|
||||
@ -77,16 +93,18 @@ bool JSONConfiguration::GetRaw(const Slice &key, std::string *value) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONConfiguration::SetRaw(const Slice &key, const Slice &value) {
|
||||
bool
|
||||
JSONConfiguration::SetRaw(const Slice &key, const Slice &value)
|
||||
{
|
||||
return SetValue(key, value.ToString());
|
||||
}
|
||||
|
||||
Configuration::Keys JSONConfiguration::Enumerate(const Slice &range) const {
|
||||
Configuration::Keys
|
||||
JSONConfiguration::Enumerate(const Slice &range) const
|
||||
{
|
||||
Configuration::Keys key_set;
|
||||
std::string prefix = range;
|
||||
if (!prefix.empty()) {
|
||||
prefix += ".";
|
||||
}
|
||||
if (!prefix.empty()) { prefix += "."; }
|
||||
|
||||
auto keys = Split(range, '.');
|
||||
auto root = object_;
|
||||
@ -96,24 +114,19 @@ Configuration::Keys JSONConfiguration::Enumerate(const Slice &range) const {
|
||||
TILE_LOG_ERROR("Invalid range: {}", range);
|
||||
return key_set;
|
||||
}
|
||||
if (!root.isMember(cur_key)) {
|
||||
return key_set;
|
||||
}
|
||||
if (!root.isMember(cur_key)) { return key_set; }
|
||||
root = root[cur_key];
|
||||
}
|
||||
|
||||
for (const auto &key : root.getMemberNames()) {
|
||||
key_set.push_back(prefix + key);
|
||||
}
|
||||
for (const auto &key : root.getMemberNames()) { key_set.push_back(prefix + key); }
|
||||
return key_set;
|
||||
}
|
||||
|
||||
bool JSONConfiguration::FindStart(const Slice &key, Slice *last_part,
|
||||
Json::Value **parent_obj) {
|
||||
bool
|
||||
JSONConfiguration::FindStart(const Slice &key, Slice *last_part, Json::Value **parent_obj)
|
||||
{
|
||||
auto keys = Split(key, '.');
|
||||
if (keys.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (keys.empty()) { return false; }
|
||||
|
||||
Json::Value *root = &object_;
|
||||
for (std::size_t i = 0; i < keys.size() - 1; ++i) {
|
||||
@ -126,8 +139,7 @@ bool JSONConfiguration::FindStart(const Slice &key, Slice *last_part,
|
||||
if (!root->isMember(cur_key)) {
|
||||
(*root)[cur_key] = Json::Value(Json::objectValue);
|
||||
} else if (!(*root)[cur_key].isObject()) {
|
||||
TILE_LOG_ERROR("only leaf nodes can be set: key: {}, cur_key(idx={}): {}",
|
||||
key, i, cur_key);
|
||||
TILE_LOG_ERROR("only leaf nodes can be set: key: {}, cur_key(idx={}): {}", key, i, cur_key);
|
||||
return false;
|
||||
}
|
||||
root = &(*root)[cur_key];
|
||||
|
@ -30,17 +30,18 @@ protected:
|
||||
|
||||
private:
|
||||
bool FindStart(const Slice &key, Slice *last_prt, Json::Value **parent_obj);
|
||||
template <typename T> bool SetValue(const Slice &key, T value);
|
||||
template<typename T>
|
||||
bool SetValue(const Slice &key, T value);
|
||||
Json::Value object_;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool JSONConfiguration::SetValue(const Slice &key, T value) {
|
||||
bool
|
||||
JSONConfiguration::SetValue(const Slice &key, T value)
|
||||
{
|
||||
Slice last_part;
|
||||
Json::Value *root;
|
||||
if (!FindStart(key, &last_part, &root)) {
|
||||
return false;
|
||||
}
|
||||
if (!FindStart(key, &last_part, &root)) { return false; }
|
||||
(*root)[last_part] = Json::Value(value);
|
||||
return true;
|
||||
}
|
||||
|
@ -18,12 +18,15 @@ const char *kJsonConfig = R"(
|
||||
)";
|
||||
}
|
||||
|
||||
TEST(JSONConfiguration, Load) {
|
||||
TEST(JSONConfiguration, Load)
|
||||
{
|
||||
JSONConfiguration config;
|
||||
std::istringstream istr(kJsonConfig);
|
||||
ASSERT_TRUE(config.load(istr));
|
||||
}
|
||||
TEST(JSONConfiguration, Has) {
|
||||
|
||||
TEST(JSONConfiguration, Has)
|
||||
{
|
||||
JSONConfiguration config;
|
||||
std::istringstream istr(kJsonConfig);
|
||||
ASSERT_TRUE(config.load(istr));
|
||||
@ -37,7 +40,8 @@ TEST(JSONConfiguration, Has) {
|
||||
ASSERT_TRUE(config.Has("key5.key52"));
|
||||
}
|
||||
|
||||
TEST(JSONConfiguration, SampleSet) {
|
||||
TEST(JSONConfiguration, SampleSet)
|
||||
{
|
||||
JSONConfiguration config;
|
||||
std::istringstream istr(kJsonConfig);
|
||||
ASSERT_TRUE(config.load(istr));
|
||||
@ -57,7 +61,8 @@ TEST(JSONConfiguration, SampleSet) {
|
||||
ASSERT_FALSE(*config.GetBool("key4")) << config.Dump();
|
||||
}
|
||||
|
||||
TEST(JSONConfiguration, LayeredSet) {
|
||||
TEST(JSONConfiguration, LayeredSet)
|
||||
{
|
||||
JSONConfiguration config;
|
||||
std::istringstream istr(kJsonConfig);
|
||||
ASSERT_TRUE(config.load(istr));
|
||||
@ -73,7 +78,8 @@ TEST(JSONConfiguration, LayeredSet) {
|
||||
ASSERT_FALSE(*config.GetBool("key5.key52")) << config.Dump();
|
||||
}
|
||||
|
||||
TEST(json_configuration, Enumerate) {
|
||||
TEST(json_configuration, Enumerate)
|
||||
{
|
||||
JSONConfiguration config;
|
||||
std::istringstream istr(kJsonConfig);
|
||||
ASSERT_TRUE(config.load(istr));
|
||||
|
@ -3,33 +3,42 @@
|
||||
namespace tile {
|
||||
|
||||
LayeredConfiguration::LayeredConfiguration() {}
|
||||
|
||||
LayeredConfiguration::~LayeredConfiguration() {}
|
||||
|
||||
void LayeredConfiguration::Add(Configuration::Ptr cfg) {
|
||||
void
|
||||
LayeredConfiguration::Add(Configuration::Ptr cfg)
|
||||
{
|
||||
Add(cfg, highest(), false);
|
||||
}
|
||||
|
||||
void LayeredConfiguration::Add(Configuration::Ptr cfg, int priority) {
|
||||
void
|
||||
LayeredConfiguration::Add(Configuration::Ptr cfg, int priority)
|
||||
{
|
||||
Add(cfg, priority, false);
|
||||
}
|
||||
|
||||
void LayeredConfiguration::Add(Configuration::Ptr cfg,
|
||||
const std::string &label) {
|
||||
void
|
||||
LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label)
|
||||
{
|
||||
Add(cfg, label, highest(), false);
|
||||
}
|
||||
|
||||
void LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label,
|
||||
int priority) {
|
||||
void
|
||||
LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label, int priority)
|
||||
{
|
||||
Add(cfg, label, priority, false);
|
||||
}
|
||||
|
||||
void LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label,
|
||||
bool writeable) {
|
||||
void
|
||||
LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label, bool writeable)
|
||||
{
|
||||
Add(cfg, label, highest(), writeable);
|
||||
}
|
||||
|
||||
void LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label,
|
||||
int priority, bool writeable) {
|
||||
void
|
||||
LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label, int priority, bool writeable)
|
||||
{
|
||||
Configuration::ScopedLock lock(*this);
|
||||
ConfigItem item;
|
||||
item.cfg = cfg;
|
||||
@ -37,32 +46,36 @@ void LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label,
|
||||
item.writeable = writeable;
|
||||
item.label = label;
|
||||
auto it = configs_.begin();
|
||||
while (it != configs_.end() && it->priority < priority) {
|
||||
++it;
|
||||
}
|
||||
while (it != configs_.end() && it->priority < priority) { ++it; }
|
||||
configs_.insert(it, item);
|
||||
}
|
||||
|
||||
void LayeredConfiguration::Add(Configuration::Ptr cfg, int priority,
|
||||
bool writeable) {
|
||||
void
|
||||
LayeredConfiguration::Add(Configuration::Ptr cfg, int priority, bool writeable)
|
||||
{
|
||||
Add(cfg, std::string(), priority, writeable);
|
||||
}
|
||||
void LayeredConfiguration::AddWriteable(Configuration::Ptr cfg, int priority) {
|
||||
|
||||
void
|
||||
LayeredConfiguration::AddWriteable(Configuration::Ptr cfg, int priority)
|
||||
{
|
||||
Add(cfg, priority, true);
|
||||
}
|
||||
|
||||
Configuration::Ptr LayeredConfiguration::Find(const Slice &label) const {
|
||||
Configuration::Ptr
|
||||
LayeredConfiguration::Find(const Slice &label) const
|
||||
{
|
||||
Configuration::ScopedLock lock(*this);
|
||||
|
||||
for (const auto &conf : configs_) {
|
||||
if (conf.label == label) {
|
||||
return conf.cfg;
|
||||
}
|
||||
if (conf.label == label) { return conf.cfg; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LayeredConfiguration::RemoveConfig(Configuration::Ptr cfg) {
|
||||
void
|
||||
LayeredConfiguration::RemoveConfig(Configuration::Ptr cfg)
|
||||
{
|
||||
Configuration::ScopedLock lock(*this);
|
||||
for (auto it = configs_.begin(); it != configs_.end();) {
|
||||
if (it->cfg == cfg) {
|
||||
@ -73,54 +86,59 @@ void LayeredConfiguration::RemoveConfig(Configuration::Ptr cfg) {
|
||||
}
|
||||
}
|
||||
|
||||
bool LayeredConfiguration::GetRaw(const Slice &key, std::string *value) const {
|
||||
bool
|
||||
LayeredConfiguration::GetRaw(const Slice &key, std::string *value) const
|
||||
{
|
||||
for (const auto &conf : configs_) {
|
||||
if (conf.cfg->GetRaw(key, value)) {
|
||||
return true;
|
||||
}
|
||||
if (conf.cfg->GetRaw(key, value)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LayeredConfiguration::SetRaw(const Slice &key, const Slice &value) {
|
||||
bool
|
||||
LayeredConfiguration::SetRaw(const Slice &key, const Slice &value)
|
||||
{
|
||||
for (const auto &conf : configs_) {
|
||||
if (conf.writeable && conf.cfg->SetRaw(key, value)) {
|
||||
return true;
|
||||
}
|
||||
if (conf.writeable && conf.cfg->SetRaw(key, value)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Configuration::Keys LayeredConfiguration::Enumerate(const Slice &key) const {
|
||||
Configuration::Keys
|
||||
LayeredConfiguration::Enumerate(const Slice &key) const
|
||||
{
|
||||
Configuration::Keys keys;
|
||||
std::set<Slice> key_set;
|
||||
for (const auto &conf : configs_) {
|
||||
auto conf_keys = conf.cfg->Enumerate(key);
|
||||
for (const auto &k : conf_keys) {
|
||||
if (key_set.insert(k).second) {
|
||||
keys.push_back(k);
|
||||
}
|
||||
if (key_set.insert(k).second) { keys.push_back(k); }
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
void LayeredConfiguration::RemoveRaw(const std::string &key) {
|
||||
void
|
||||
LayeredConfiguration::RemoveRaw(const std::string &key)
|
||||
{
|
||||
for (auto &conf : configs_) {
|
||||
if (conf.writeable) {
|
||||
conf.cfg->RemoveRaw(key);
|
||||
}
|
||||
if (conf.writeable) { conf.cfg->RemoveRaw(key); }
|
||||
}
|
||||
}
|
||||
|
||||
int LayeredConfiguration::lowest() const {
|
||||
int
|
||||
LayeredConfiguration::lowest() const
|
||||
{
|
||||
if (configs_.empty()) {
|
||||
return 0;
|
||||
} else {
|
||||
return configs_.front().priority - 1;
|
||||
}
|
||||
}
|
||||
int LayeredConfiguration::highest() const {
|
||||
|
||||
int
|
||||
LayeredConfiguration::highest() const
|
||||
{
|
||||
if (configs_.empty()) {
|
||||
return 0;
|
||||
} else {
|
||||
|
@ -19,8 +19,7 @@ public:
|
||||
void Add(Configuration::Ptr cfg, const std::string &label);
|
||||
void Add(Configuration::Ptr cfg, const std::string &label, int priority);
|
||||
void Add(Configuration::Ptr cfg, const std::string &label, bool writeable);
|
||||
void Add(Configuration::Ptr cfg, const std::string &label, int priority,
|
||||
bool writeable);
|
||||
void Add(Configuration::Ptr cfg, const std::string &label, int priority, bool writeable);
|
||||
void Add(Configuration::Ptr cfg, int priority, bool writeable);
|
||||
void AddWriteable(Configuration::Ptr cfg, int priority);
|
||||
Configuration::Ptr Find(const Slice &label) const;
|
||||
|
@ -12,24 +12,30 @@
|
||||
namespace tile {
|
||||
|
||||
template<typename T>
|
||||
auto ToJson(const T &value)
|
||||
-> enable_if_t<std::is_unsigned<T>::value, Json::Value> {
|
||||
auto
|
||||
ToJson(const T &value) -> enable_if_t<std::is_unsigned<T>::value, Json::Value>
|
||||
{
|
||||
return static_cast<std::uint64_t>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto ToJson(const T &value)
|
||||
-> enable_if_t<std::is_signed<T>::value, Json::Value> {
|
||||
auto
|
||||
ToJson(const T &value) -> enable_if_t<std::is_signed<T>::value, Json::Value>
|
||||
{
|
||||
return static_cast<std::int64_t>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto ToJson(const T &value)
|
||||
-> enable_if_t<!std::is_signed<T>::value && !std::is_unsigned<T>::value,
|
||||
Json::Value> {
|
||||
auto
|
||||
ToJson(const T &value) -> enable_if_t<!std::is_signed<T>::value && !std::is_unsigned<T>::value, Json::Value>
|
||||
{
|
||||
return Format("{}", value);
|
||||
}
|
||||
|
||||
template <typename T> Json::Value ToJson(const std::atomic<T> &v) {
|
||||
template<typename T>
|
||||
Json::Value
|
||||
ToJson(const std::atomic<T> &v)
|
||||
{
|
||||
return ToJson(v.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,11 @@ namespace tile {
|
||||
class ScopedDeferred {
|
||||
public:
|
||||
template<typename F>
|
||||
explicit ScopedDeferred(F &&f) : action_(std::move(f)) {}
|
||||
explicit ScopedDeferred(F &&f) : action_(std::move(f))
|
||||
{}
|
||||
|
||||
~ScopedDeferred() { action_(); }
|
||||
|
||||
ScopedDeferred(const ScopedDeferred &) = delete;
|
||||
ScopedDeferred &operator=(const ScopedDeferred &) = delete;
|
||||
|
||||
@ -23,34 +25,33 @@ private:
|
||||
class Deferred {
|
||||
public:
|
||||
Deferred() = default;
|
||||
template <typename F>
|
||||
explicit Deferred(F &&f) : action_(std::forward<F>(f)) {}
|
||||
|
||||
Deferred(Deferred &&other) noexcept : action_(std::move(other.action_)) {
|
||||
other.action_ = nullptr;
|
||||
}
|
||||
Deferred &operator=(Deferred &&other) noexcept {
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
template<typename F>
|
||||
explicit Deferred(F &&f) : action_(std::forward<F>(f))
|
||||
{}
|
||||
|
||||
Deferred(Deferred &&other) noexcept : action_(std::move(other.action_)) { other.action_ = nullptr; }
|
||||
|
||||
Deferred &operator=(Deferred &&other) noexcept
|
||||
{
|
||||
if (&other == this) { return *this; }
|
||||
|
||||
Fire();
|
||||
action_ = std::move(other.action_);
|
||||
other.action_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
~Deferred() {
|
||||
if (action_) {
|
||||
action_();
|
||||
}
|
||||
|
||||
~Deferred()
|
||||
{
|
||||
if (action_) { action_(); }
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept { return !!action_; }
|
||||
|
||||
void Fire() noexcept {
|
||||
if (auto op = std::move(action_)) {
|
||||
op();
|
||||
}
|
||||
void Fire() noexcept
|
||||
{
|
||||
if (auto op = std::move(action_)) { op(); }
|
||||
}
|
||||
|
||||
void Dimiss() noexcept { action_ = nullptr; }
|
||||
|
@ -2,7 +2,8 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace tile {
|
||||
TEST(ScopedDefered, All) {
|
||||
TEST(ScopedDefered, All)
|
||||
{
|
||||
bool f = false;
|
||||
{
|
||||
ScopedDeferred scoped_defered([&] { f = true; });
|
||||
@ -11,7 +12,8 @@ TEST(ScopedDefered, All) {
|
||||
ASSERT_TRUE(f);
|
||||
}
|
||||
|
||||
TEST(Defered, All) {
|
||||
TEST(Defered, All)
|
||||
{
|
||||
bool f1 = false;
|
||||
bool f2 = false;
|
||||
{
|
||||
|
@ -10,16 +10,16 @@
|
||||
|
||||
namespace tile {
|
||||
|
||||
std::string Demangle(const char *s) {
|
||||
std::string
|
||||
Demangle(const char *s)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
return s;
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
int status;
|
||||
char *demangled = abi::__cxa_demangle(s, nullptr, nullptr, &status);
|
||||
ScopedDeferred _([&] { free(demangled); });
|
||||
if (!demangled) {
|
||||
return s;
|
||||
}
|
||||
if (!demangled) { return s; }
|
||||
return demangled;
|
||||
#else
|
||||
#error "Demangle not supported on current compiler."
|
||||
|
@ -11,7 +11,10 @@ namespace tile {
|
||||
|
||||
std::string Demangle(const char *s);
|
||||
|
||||
template <class T> std::string GetTypeName() {
|
||||
template<class T>
|
||||
std::string
|
||||
GetTypeName()
|
||||
{
|
||||
return Demangle(typeid(T).name());
|
||||
}
|
||||
|
||||
@ -20,7 +23,10 @@ template <class T> std::string GetTypeName() {
|
||||
#pragma GCC diagnostic ignored "-Wnonnull-compare"
|
||||
#endif
|
||||
|
||||
template <class T> std::string GetTypeName(T &&o) {
|
||||
template<class T>
|
||||
std::string
|
||||
GetTypeName(T &&o)
|
||||
{
|
||||
return Demangle(typeid(std::forward<T>(o)).name());
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,11 @@ struct C {
|
||||
};
|
||||
};
|
||||
|
||||
TEST(Demangle, All) {
|
||||
TEST(Demangle, All)
|
||||
{
|
||||
ASSERT_EQ("tile::C::D::E", GetTypeName<C::D::E>());
|
||||
ASSERT_NE(GetTypeName<C::D>(), typeid(C::D::E).name());
|
||||
ASSERT_EQ("invalid function name !@#$",
|
||||
Demangle("invalid function name !@#$"));
|
||||
ASSERT_EQ("invalid function name !@#$", Demangle("invalid function name !@#$"));
|
||||
}
|
||||
|
||||
}// namespace tile
|
||||
|
@ -22,54 +22,45 @@
|
||||
|
||||
// class dependency
|
||||
#define TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \
|
||||
extern ::tile::detail::dependency_registry::ClassRegistry< \
|
||||
struct Registry, Interface, ##__VA_ARGS__> &Registry
|
||||
extern ::tile::detail::dependency_registry::ClassRegistry<struct Registry, Interface, ##__VA_ARGS__> &Registry
|
||||
|
||||
#define TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \
|
||||
::tile::detail::dependency_registry::ClassRegistry< \
|
||||
struct Registry, Interface, ##__VA_ARGS__> &Registry = \
|
||||
::tile::detail::dependency_registry::ClassRegistry<struct Registry, Interface, ##__VA_ARGS__> &Registry = \
|
||||
**::tile::internal::LazyInit<::tile::NeverDestroyed< \
|
||||
::tile::detail::dependency_registry::ClassRegistry< \
|
||||
struct Registry, Interface, ##__VA_ARGS__>>>()
|
||||
::tile::detail::dependency_registry::ClassRegistry<struct Registry, Interface, ##__VA_ARGS__>>>()
|
||||
|
||||
#define TILE_REGISTER_CLASS_DEPENDENCY(Registry, ImplementationName, \
|
||||
ImplementationClassName, ...) \
|
||||
#define TILE_REGISTER_CLASS_DEPENDENCY(Registry, ImplementationName, ImplementationClassName, ...) \
|
||||
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY( \
|
||||
Registry, ImplementationName, \
|
||||
TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE( \
|
||||
ImplementationClassName, ##__VA_ARGS__))
|
||||
TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE(ImplementationClassName, ##__VA_ARGS__))
|
||||
|
||||
#define TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(Registry, Name, Factory) \
|
||||
__attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \
|
||||
tile_reserved_registry_dependency_class_register_, __COUNTER__)() { \
|
||||
auto registry = ::tile::internal::LazyInit<::tile::NeverDestroyed< \
|
||||
typename std::decay<decltype(Registry)>::type>>() \
|
||||
tile_reserved_registry_dependency_class_register_, __COUNTER__)() \
|
||||
{ \
|
||||
auto registry = \
|
||||
::tile::internal::LazyInit<::tile::NeverDestroyed<typename std::decay<decltype(Registry)>::type>>() \
|
||||
->Get(); \
|
||||
::tile::detail::dependency_registry::AddToRegistry(registry, Name, \
|
||||
Factory); \
|
||||
::tile::detail::dependency_registry::AddToRegistry(registry, Name, Factory); \
|
||||
}
|
||||
|
||||
// object dependency
|
||||
#define TILE_DECLARE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \
|
||||
extern ::tile::detail::dependency_registry::ObjectRegistry< \
|
||||
struct Registry, Interface> &Registry
|
||||
extern ::tile::detail::dependency_registry::ObjectRegistry<struct Registry, Interface> &Registry
|
||||
|
||||
#define TILE_DEFINE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \
|
||||
::tile::detail::dependency_registry::ObjectRegistry<struct Registry, \
|
||||
Interface> &Registry = \
|
||||
**::tile::internal::LazyInit<::tile::NeverDestroyed< \
|
||||
::tile::detail::dependency_registry::ObjectRegistry<struct Registry, \
|
||||
Interface>>>()
|
||||
::tile::detail::dependency_registry::ObjectRegistry<struct Registry, Interface> &Registry = \
|
||||
**::tile::internal::LazyInit< \
|
||||
::tile::NeverDestroyed<::tile::detail::dependency_registry::ObjectRegistry<struct Registry, Interface>>>()
|
||||
|
||||
#define TILE_REGISTER_OBJECT_DEPENDENCY(Registry, ObjectName, \
|
||||
PointerOrFactory) \
|
||||
#define TILE_REGISTER_OBJECT_DEPENDENCY(Registry, ObjectName, PointerOrFactory) \
|
||||
__attribute__((constructor)) static void TILE_INTERNAL_PP_CAT( \
|
||||
tile_reserved_registry_dependency_object_register_, __COUNTER__)() { \
|
||||
auto registry = ::tile::internal::LazyInit<::tile::NeverDestroyed< \
|
||||
typename std::decay<decltype(Registry)>::type>>() \
|
||||
tile_reserved_registry_dependency_object_register_, __COUNTER__)() \
|
||||
{ \
|
||||
auto registry = \
|
||||
::tile::internal::LazyInit<::tile::NeverDestroyed<typename std::decay<decltype(Registry)>::type>>() \
|
||||
->Get(); \
|
||||
::tile::detail::dependency_registry::AddToRegistry(registry, ObjectName, \
|
||||
PointerOrFactory); \
|
||||
::tile::detail::dependency_registry::AddToRegistry(registry, ObjectName, PointerOrFactory); \
|
||||
}
|
||||
|
||||
namespace tile {
|
||||
@ -78,13 +69,18 @@ namespace dependency_registry {
|
||||
|
||||
#define TILE_REGISTER_CLASS_DEPENDENCY_FUNCTION_AUX_INVOKE(Interface, ...) \
|
||||
::tile::detail::dependency_registry::FactoryAux<Interface, ##__VA_ARGS__>
|
||||
|
||||
template<typename Interface, typename... Args>
|
||||
std::unique_ptr<Interface> FactoryAux(Args &&...args) {
|
||||
std::unique_ptr<Interface>
|
||||
FactoryAux(Args &&...args)
|
||||
{
|
||||
return make_unique<Interface>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void AddToRegistry(T *registry, Args &&...args) {
|
||||
void
|
||||
AddToRegistry(T *registry, Args &&...args)
|
||||
{
|
||||
registry->Register(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@ -93,41 +89,37 @@ class ClassRegistry {
|
||||
using Factory = std::function<std::unique_ptr<Interface>(FactoryArgs...)>;
|
||||
|
||||
public:
|
||||
Factory TryGetFactory(Slice name) const noexcept {
|
||||
Factory TryGetFactory(Slice name) const noexcept
|
||||
{
|
||||
auto iter = factories_.find(name.ToString());
|
||||
if (iter == factories_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (iter == factories_.end()) { return nullptr; }
|
||||
|
||||
auto constructor_ptr = iter->second.get();
|
||||
return [constructor_ptr](FactoryArgs... args) {
|
||||
return (*constructor_ptr)(std::forward<FactoryArgs>(args)...);
|
||||
};
|
||||
return
|
||||
[constructor_ptr](FactoryArgs... args) { return (*constructor_ptr)(std::forward<FactoryArgs>(args)...); };
|
||||
}
|
||||
|
||||
Factory GetFactory(const std::string &name) const noexcept {
|
||||
Factory GetFactory(const std::string &name) const noexcept
|
||||
{
|
||||
auto result = TryGetFactory(name);
|
||||
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.",
|
||||
name, GetTypeName<Interface>());
|
||||
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName<Interface>());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<Interface> TryNew(Slice name, FactoryArgs... args) const {
|
||||
std::unique_ptr<Interface> TryNew(Slice name, FactoryArgs... args) const
|
||||
{
|
||||
auto iter = factories_.find(name.ToString());
|
||||
if (iter == factories_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (iter == factories_.end()) { return nullptr; }
|
||||
|
||||
auto &constructor = iter->second;
|
||||
return (*constructor)(std::forward<FactoryArgs>(args)...);
|
||||
}
|
||||
|
||||
std::unique_ptr<Interface> New(const std::string &name,
|
||||
FactoryArgs... args) const {
|
||||
std::unique_ptr<Interface> New(const std::string &name, FactoryArgs... args) const
|
||||
{
|
||||
auto result = TryNew(name, std::forward<FactoryArgs>(args)...);
|
||||
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.",
|
||||
name, GetTypeName<Interface>());
|
||||
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName<Interface>());
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -135,9 +127,9 @@ private:
|
||||
template<typename T, typename... Args>
|
||||
friend void AddToRegistry(T *registry, Args &&...args);
|
||||
|
||||
void Register(const std::string &name, Factory factory) {
|
||||
TILE_CHECK(factories_.find(name) == factories_.end(),
|
||||
"Duplicate class dependency: ");
|
||||
void Register(const std::string &name, Factory factory)
|
||||
{
|
||||
TILE_CHECK(factories_.find(name) == factories_.end(), "Duplicate class dependency: ");
|
||||
factories_[name] = make_unique<Factory>(std::move(factory));
|
||||
}
|
||||
|
||||
@ -145,23 +137,22 @@ private:
|
||||
std::unordered_map<std::string, std::unique_ptr<Factory>> factories_;
|
||||
};
|
||||
|
||||
template <typename Tag, typename Interface> class ObjectRegistry {
|
||||
template<typename Tag, typename Interface>
|
||||
class ObjectRegistry {
|
||||
public:
|
||||
Interface *TryGet(Slice name) const {
|
||||
Interface *TryGet(Slice name) const
|
||||
{
|
||||
auto iter = objects_.find(name.ToString());
|
||||
if (iter == objects_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (iter == objects_.end()) { return nullptr; }
|
||||
auto &&e = *iter->second;
|
||||
std::call_once(e.flag, [&] { e.object = e.initializer(); });
|
||||
return e.object.Get();
|
||||
}
|
||||
|
||||
Interface *Get(Slice name) const {
|
||||
Interface *Get(Slice name) const
|
||||
{
|
||||
auto result = TryGet(name);
|
||||
TILE_CHECK(
|
||||
result,
|
||||
"Object dependency [{}] implementing interface [{}] is not found", name,
|
||||
TILE_CHECK(result, "Object dependency [{}] implementing interface [{}] is not found", name,
|
||||
GetTypeName<Interface>());
|
||||
return result;
|
||||
}
|
||||
@ -170,19 +161,17 @@ private:
|
||||
template<typename T, typename... Args>
|
||||
friend void AddToRegistry(T *registry, Args &&...args);
|
||||
|
||||
void Register(const std::string &name, Interface *object) {
|
||||
void Register(const std::string &name, Interface *object)
|
||||
{
|
||||
TILE_DCHECK(objects_.find(name) == objects_.end());
|
||||
auto &&e = objects_[name];
|
||||
e = make_unique<LazilyInstantiatedObject>();
|
||||
std::call_once(e->flag, [&] {
|
||||
e->object = MaybeOwning<Interface>(non_owning, object);
|
||||
});
|
||||
std::call_once(e->flag, [&] { e->object = MaybeOwning<Interface>(non_owning, object); });
|
||||
}
|
||||
|
||||
void Register(const std::string &name,
|
||||
std::function<MaybeOwning<Interface>()> initializer) {
|
||||
TILE_CHECK(objects_.find(name) == objects_.end(),
|
||||
"Double registration of object dependency [{}]", name);
|
||||
void Register(const std::string &name, std::function<MaybeOwning<Interface>()> initializer)
|
||||
{
|
||||
TILE_CHECK(objects_.find(name) == objects_.end(), "Double registration of object dependency [{}]", name);
|
||||
|
||||
objects_[name] = make_unique<LazilyInstantiatedObject>();
|
||||
objects_[name]->initializer = std::move(initializer);
|
||||
@ -195,8 +184,7 @@ private:
|
||||
std::function<MaybeOwning<Interface>()> initializer;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::unique_ptr<LazilyInstantiatedObject>>
|
||||
objects_;
|
||||
std::unordered_map<std::string, std::unique_ptr<LazilyInstantiatedObject>> objects_;
|
||||
};
|
||||
|
||||
}// namespace dependency_registry
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "tile/base/dependency_registry.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace tile {
|
||||
struct Destroyer {
|
||||
virtual ~Destroyer() = default;
|
||||
@ -7,27 +8,33 @@ struct Destroyer {
|
||||
|
||||
struct FastDestroyer : Destroyer {
|
||||
FastDestroyer() { ++instances; }
|
||||
|
||||
~FastDestroyer() override { --instances; }
|
||||
|
||||
static int instances;
|
||||
};
|
||||
|
||||
int FastDestroyer::instances = 0;
|
||||
|
||||
struct GentleDestroyer : Destroyer {
|
||||
GentleDestroyer() { ++instances; }
|
||||
|
||||
~GentleDestroyer() override { --instances; }
|
||||
|
||||
static int instances;
|
||||
};
|
||||
|
||||
int GentleDestroyer::instances = 0;
|
||||
|
||||
struct SpeedDestroyer : Destroyer {
|
||||
explicit SpeedDestroyer(int) {}
|
||||
};
|
||||
|
||||
struct SpeedDestroyer2 : Destroyer {};
|
||||
|
||||
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer);
|
||||
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(world_destroyer, Destroyer);
|
||||
TILE_REGISTER_CLASS_DEPENDENCY(world_destroyer, "fast-destroyer",
|
||||
FastDestroyer);
|
||||
TILE_REGISTER_CLASS_DEPENDENCY(world_destroyer, "fast-destroyer", FastDestroyer);
|
||||
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(world_destroyer, "gentle-destroyer", [] {
|
||||
return make_unique<GentleDestroyer>();
|
||||
});
|
||||
@ -35,14 +42,13 @@ GentleDestroyer global_gentle_destroyer;
|
||||
|
||||
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(with_arg_destroyer, Destroyer, int);
|
||||
TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(with_arg_destroyer, Destroyer, int);
|
||||
TILE_REGISTER_CLASS_DEPENDENCY(with_arg_destroyer, "speed-destroyer",
|
||||
SpeedDestroyer, int);
|
||||
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(with_arg_destroyer, "speed-destroyer-2",
|
||||
[](int speed) {
|
||||
TILE_REGISTER_CLASS_DEPENDENCY(with_arg_destroyer, "speed-destroyer", SpeedDestroyer, int);
|
||||
TILE_REGISTER_CLASS_DEPENDENCY_FACTORY(with_arg_destroyer, "speed-destroyer-2", [](int speed) {
|
||||
return make_unique<SpeedDestroyer2>();
|
||||
});
|
||||
|
||||
TEST(DependencyRegistry, Class) {
|
||||
TEST(DependencyRegistry, Class)
|
||||
{
|
||||
EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer"));
|
||||
EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer"));
|
||||
EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));
|
||||
@ -65,7 +71,8 @@ TEST(DependencyRegistry, Class) {
|
||||
EXPECT_EQ(0, FastDestroyer::instances);
|
||||
}
|
||||
|
||||
TEST(DependencyRegistry, ClassWithArgs) {
|
||||
TEST(DependencyRegistry, ClassWithArgs)
|
||||
{
|
||||
EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer"));
|
||||
EXPECT_TRUE(with_arg_destroyer.TryGetFactory("speed-destroyer-2"));
|
||||
EXPECT_FALSE(with_arg_destroyer.TryGetFactory("speed-destroyer-3"));
|
||||
|
@ -8,16 +8,20 @@
|
||||
#include <type_traits>
|
||||
|
||||
namespace tile {
|
||||
template <typename To, typename From,
|
||||
typename R = typename std::conditional<std::is_const<From>::value,
|
||||
const To *, To *>::type>
|
||||
inline R down_cast(From *ptr) {
|
||||
template<typename To,
|
||||
typename From,
|
||||
typename R = typename std::conditional<std::is_const<From>::value, const To *, To *>::type>
|
||||
inline R
|
||||
down_cast(From *ptr)
|
||||
{
|
||||
TILE_DCHECK(dynamic_cast<R>(ptr));
|
||||
return static_cast<R>(ptr);
|
||||
}
|
||||
|
||||
template<typename To, typename From>
|
||||
inline auto down_cast(From &ref) -> decltype(down_cast(&ref)) {
|
||||
inline auto
|
||||
down_cast(From &ref) -> decltype(down_cast(&ref))
|
||||
{
|
||||
return down_cast<To>(&ref);
|
||||
}
|
||||
}// namespace tile
|
||||
|
@ -5,9 +5,11 @@ namespace tile {
|
||||
struct A {
|
||||
virtual ~A() = default;
|
||||
};
|
||||
|
||||
struct B : A {};
|
||||
|
||||
TEST(DownCast, All) {
|
||||
TEST(DownCast, All)
|
||||
{
|
||||
B b;
|
||||
A *ptr = &b;
|
||||
ASSERT_NE(nullptr, down_cast<B>(ptr));
|
||||
@ -15,7 +17,8 @@ TEST(DownCast, All) {
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
TEST(CastDeathTest, DownCast) {
|
||||
TEST(CastDeathTest, DownCast)
|
||||
{
|
||||
A a;
|
||||
A *ptr = &a;
|
||||
ASSERT_DEATH(down_cast<B>(ptr), "");
|
||||
|
@ -21,23 +21,28 @@ static constexpr std::size_t kBufferSize = BLOCK_SIZE_1M;
|
||||
|
||||
}// namespace
|
||||
|
||||
std::string EncodeBase64(Slice from) {
|
||||
std::string
|
||||
EncodeBase64(Slice from)
|
||||
{
|
||||
std::string result;
|
||||
EncodeBase64(from, &result);
|
||||
return result;
|
||||
}
|
||||
std::optional<std::string> DecodeBase64(Slice from) {
|
||||
|
||||
std::optional<std::string>
|
||||
DecodeBase64(Slice from)
|
||||
{
|
||||
std::string result;
|
||||
if (!DecodeBase64(from, &result)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!DecodeBase64(from, &result)) { return std::nullopt; }
|
||||
return result;
|
||||
}
|
||||
|
||||
void EncodeBase64(Slice from, std::string *to) {
|
||||
void
|
||||
EncodeBase64(Slice from, std::string *to)
|
||||
{
|
||||
TILE_CHECK(from.size() < std::numeric_limits<int>::max(),
|
||||
"Not implemented: Source bytes too long. {} > max_size({})",
|
||||
from.size(), std::numeric_limits<int>::max());
|
||||
"Not implemented: Source bytes too long. {} > max_size({})", from.size(),
|
||||
std::numeric_limits<int>::max());
|
||||
|
||||
const auto size = 4 * ((from.size() + 2) / 3);
|
||||
to->resize(size);
|
||||
@ -48,15 +53,11 @@ void EncodeBase64(Slice from, std::string *to) {
|
||||
std::size_t input_len;
|
||||
while (!from.empty()) {
|
||||
input_len = std::min(kBufferSize, from.size());
|
||||
used += base64_encode_block(
|
||||
reinterpret_cast<const char *>(from.data()), input_len,
|
||||
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used,
|
||||
&state);
|
||||
used += base64_encode_block(reinterpret_cast<const char *>(from.data()), input_len,
|
||||
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, &state);
|
||||
from.RemovePrefix(input_len);
|
||||
};
|
||||
base64_encode_blockend(
|
||||
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used,
|
||||
&state);
|
||||
base64_encode_blockend(reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, &state);
|
||||
|
||||
// bool success =
|
||||
// Base64::Encode(from.data(), from.size(),
|
||||
@ -64,7 +65,9 @@ void EncodeBase64(Slice from, std::string *to) {
|
||||
// TILE_CHECK(success, "Unexpected: Failed to do base64-encode.");
|
||||
}
|
||||
|
||||
bool DecodeBase64(Slice from, std::string *to) {
|
||||
bool
|
||||
DecodeBase64(Slice from, std::string *to)
|
||||
{
|
||||
if (TILE_UNLIKELY(from.empty())) {
|
||||
to->clear();
|
||||
return true;
|
||||
@ -94,12 +97,8 @@ bool DecodeBase64(Slice from, std::string *to) {
|
||||
while (!input.empty()) {
|
||||
auto input_len = std::min(kBufferSize, from.size());
|
||||
auto encoded_len = base64_decode_block(
|
||||
input.data(), input_len,
|
||||
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used,
|
||||
&state);
|
||||
if (TILE_UNLIKELY(encoded_len == 0)) {
|
||||
return false;
|
||||
}
|
||||
input.data(), input_len, reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, &state);
|
||||
if (TILE_UNLIKELY(encoded_len == 0)) { return false; }
|
||||
used += encoded_len;
|
||||
input.RemovePrefix(input_len);
|
||||
}
|
||||
@ -139,13 +138,9 @@ bool DecodeBase64(Slice from, std::string *to) {
|
||||
// final unit of encoded output will be three characters followed by
|
||||
// one "=" padding character.
|
||||
TILE_CHECK(from.size() >= 2 && to->size() >= 2);
|
||||
if (from[from.size() - 1] == '=') {
|
||||
to->pop_back();
|
||||
}
|
||||
if (from[from.size() - 1] == '=') { to->pop_back(); }
|
||||
|
||||
if (from[from.size() - 2] == '=') {
|
||||
to->pop_back();
|
||||
}
|
||||
if (from[from.size() - 2] == '=') { to->pop_back(); }
|
||||
|
||||
// to->pop_back(); // Remove Terminating null
|
||||
|
||||
|
@ -14,7 +14,8 @@ static constexpr auto kBase64Text = "Ljw+QD8/Pz8/Pz8/";
|
||||
static constexpr auto kText2 = ".<>@???????";
|
||||
static constexpr auto kBase64Text2 = "Ljw+QD8/Pz8/Pz8=";
|
||||
|
||||
TEST(Base64, Default) {
|
||||
TEST(Base64, Default)
|
||||
{
|
||||
EXPECT_EQ(kBase64Text, EncodeBase64(kText));
|
||||
auto decoded = DecodeBase64(kBase64Text);
|
||||
ASSERT_TRUE(decoded);
|
||||
@ -22,7 +23,8 @@ TEST(Base64, Default) {
|
||||
EXPECT_FALSE(DecodeBase64("some-invalid-base64-encoded!!"));
|
||||
}
|
||||
|
||||
TEST(Base64, Padding) {
|
||||
TEST(Base64, Padding)
|
||||
{
|
||||
EXPECT_EQ(kBase64Text2, EncodeBase64(kText2));
|
||||
|
||||
auto decoded = DecodeBase64(kBase64Text2);
|
||||
@ -30,14 +32,16 @@ TEST(Base64, Padding) {
|
||||
EXPECT_EQ(kText2, *decoded);
|
||||
}
|
||||
|
||||
TEST(Base64, Empty) {
|
||||
TEST(Base64, Empty)
|
||||
{
|
||||
EXPECT_EQ("", EncodeBase64(""));
|
||||
auto decoded = DecodeBase64("");
|
||||
ASSERT_TRUE(decoded);
|
||||
EXPECT_EQ("", *decoded);
|
||||
}
|
||||
|
||||
TEST(Base64, AutoPadding) {
|
||||
TEST(Base64, AutoPadding)
|
||||
{
|
||||
static constexpr auto kChar = "A";
|
||||
|
||||
EXPECT_EQ("QQ==", EncodeBase64(kChar));
|
||||
@ -63,7 +67,8 @@ TEST(Base64, AutoPadding) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Base64, Torture) {
|
||||
TEST(Base64, Torture)
|
||||
{
|
||||
std::string random_str;
|
||||
std::string encoded;
|
||||
std::string decoded;
|
||||
|
@ -3,44 +3,50 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
const char kBase64Alphabet[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
namespace {
|
||||
static inline unsigned char b64_lookup_aux(unsigned char c) {
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return c - 'A';
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return c - 71;
|
||||
if (c >= '0' && c <= '9')
|
||||
return c + 4;
|
||||
if (c == '+')
|
||||
return 62;
|
||||
if (c == '/')
|
||||
return 63;
|
||||
static inline unsigned char
|
||||
b64_lookup_aux(unsigned char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||
if (c >= 'a' && c <= 'z') return c - 71;
|
||||
if (c >= '0' && c <= '9') return c + 4;
|
||||
if (c == '+') return 62;
|
||||
if (c == '/') return 63;
|
||||
return 255;
|
||||
}
|
||||
|
||||
struct Base64LookupInitializer {
|
||||
Base64LookupInitializer() {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
kBase64Lookup[i] = b64_lookup_aux(i);
|
||||
}
|
||||
Base64LookupInitializer()
|
||||
{
|
||||
for (int i = 0; i < 256; i++) { kBase64Lookup[i] = b64_lookup_aux(i); }
|
||||
}
|
||||
|
||||
int kBase64Lookup[256];
|
||||
};
|
||||
|
||||
static inline unsigned char b64_lookup_aux1(unsigned char c) {
|
||||
static inline unsigned char
|
||||
b64_lookup_aux1(unsigned char c)
|
||||
{
|
||||
static Base64LookupInitializer init;
|
||||
return init.kBase64Lookup[c];
|
||||
}
|
||||
}// namespace
|
||||
inline unsigned char b64_lookup(unsigned char c) { return b64_lookup_aux1(c); }
|
||||
|
||||
inline unsigned char
|
||||
b64_lookup(unsigned char c)
|
||||
{
|
||||
return b64_lookup_aux1(c);
|
||||
}
|
||||
|
||||
class Base64 {
|
||||
public:
|
||||
static bool Encode(const std::string &in, std::string *out) {
|
||||
static bool Encode(const std::string &in, std::string *out)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
size_t enc_len = 0;
|
||||
unsigned char a3[3];
|
||||
@ -56,35 +62,27 @@ public:
|
||||
if (i == 3) {
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
(*out)[enc_len++] = kBase64Alphabet[a4[i]];
|
||||
}
|
||||
for (i = 0; i < 4; i++) { (*out)[enc_len++] = kBase64Alphabet[a4[i]]; }
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 3; j++) {
|
||||
a3[j] = '\0';
|
||||
}
|
||||
for (j = i; j < 3; j++) { a3[j] = '\0'; }
|
||||
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
for (j = 0; j < i + 1; j++) {
|
||||
(*out)[enc_len++] = kBase64Alphabet[a4[j]];
|
||||
}
|
||||
for (j = 0; j < i + 1; j++) { (*out)[enc_len++] = kBase64Alphabet[a4[j]]; }
|
||||
|
||||
while ((i++ < 3)) {
|
||||
(*out)[enc_len++] = '=';
|
||||
}
|
||||
while ((i++ < 3)) { (*out)[enc_len++] = '='; }
|
||||
}
|
||||
|
||||
return (enc_len == out->size());
|
||||
}
|
||||
|
||||
static bool Encode(const char *input, size_t input_length, char *out,
|
||||
size_t out_length) {
|
||||
static bool Encode(const char *input, size_t input_length, char *out, size_t out_length)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
char *out_begin = out;
|
||||
unsigned char a3[3];
|
||||
@ -92,17 +90,14 @@ public:
|
||||
|
||||
size_t encoded_length = EncodedLength(input_length);
|
||||
|
||||
if (out_length < encoded_length)
|
||||
return false;
|
||||
if (out_length < encoded_length) return false;
|
||||
|
||||
while (input_length--) {
|
||||
a3[i++] = *input++;
|
||||
if (i == 3) {
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
*out++ = kBase64Alphabet[a4[i]];
|
||||
}
|
||||
for (i = 0; i < 4; i++) { *out++ = kBase64Alphabet[a4[i]]; }
|
||||
// out[0] = kBase64Alphabet[a4[0]];
|
||||
// out[1] = kBase64Alphabet[a4[1]];
|
||||
// out[2] = kBase64Alphabet[a4[2]];
|
||||
@ -114,25 +109,20 @@ public:
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 3; j++) {
|
||||
a3[j] = '\0';
|
||||
}
|
||||
for (j = i; j < 3; j++) { a3[j] = '\0'; }
|
||||
|
||||
a3_to_a4(a4, a3);
|
||||
|
||||
for (j = 0; j < i + 1; j++) {
|
||||
*out++ = kBase64Alphabet[a4[j]];
|
||||
}
|
||||
for (j = 0; j < i + 1; j++) { *out++ = kBase64Alphabet[a4[j]]; }
|
||||
|
||||
while ((i++ < 3)) {
|
||||
*out++ = '=';
|
||||
}
|
||||
while ((i++ < 3)) { *out++ = '='; }
|
||||
}
|
||||
|
||||
return (out == (out_begin + encoded_length));
|
||||
}
|
||||
|
||||
static bool Decode(const std::string &in, std::string *out) {
|
||||
static bool Decode(const std::string &in, std::string *out)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
size_t dec_len = 0;
|
||||
unsigned char a3[3];
|
||||
@ -144,47 +134,35 @@ public:
|
||||
out->resize(DecodedLength(in));
|
||||
|
||||
while (input_len--) {
|
||||
if (*input == '=') {
|
||||
break;
|
||||
}
|
||||
if (*input == '=') { break; }
|
||||
|
||||
a4[i++] = *(input++);
|
||||
if (i == 4) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
a4[i] = b64_lookup(a4[i]);
|
||||
}
|
||||
for (i = 0; i < 4; i++) { a4[i] = b64_lookup(a4[i]); }
|
||||
|
||||
a4_to_a3(a3, a4);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
(*out)[dec_len++] = a3[i];
|
||||
}
|
||||
for (i = 0; i < 3; i++) { (*out)[dec_len++] = a3[i]; }
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 4; j++) {
|
||||
a4[j] = '\0';
|
||||
}
|
||||
for (j = i; j < 4; j++) { a4[j] = '\0'; }
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
a4[j] = b64_lookup(a4[j]);
|
||||
}
|
||||
for (j = 0; j < 4; j++) { a4[j] = b64_lookup(a4[j]); }
|
||||
|
||||
a4_to_a3(a3, a4);
|
||||
|
||||
for (j = 0; j < i - 1; j++) {
|
||||
(*out)[dec_len++] = a3[j];
|
||||
}
|
||||
for (j = 0; j < i - 1; j++) { (*out)[dec_len++] = a3[j]; }
|
||||
}
|
||||
|
||||
return (dec_len == out->size());
|
||||
}
|
||||
|
||||
static bool Decode(const char *input, size_t input_length, char *out,
|
||||
size_t out_length) {
|
||||
static bool Decode(const char *input, size_t input_length, char *out, size_t out_length)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
char *out_begin = out;
|
||||
unsigned char a3[3];
|
||||
@ -192,93 +170,76 @@ public:
|
||||
|
||||
size_t decoded_length = DecodedLength(input, input_length);
|
||||
|
||||
if (out_length < decoded_length)
|
||||
return false;
|
||||
if (out_length < decoded_length) return false;
|
||||
|
||||
while (input_length--) {
|
||||
if (*input == '=') {
|
||||
break;
|
||||
}
|
||||
if (*input == '=') { break; }
|
||||
|
||||
a4[i++] = *(input++);
|
||||
if (i == 4) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
a4[i] = b64_lookup(a4[i]);
|
||||
}
|
||||
for (i = 0; i < 4; i++) { a4[i] = b64_lookup(a4[i]); }
|
||||
|
||||
a4_to_a3(a3, a4);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
*out++ = a3[i];
|
||||
}
|
||||
for (i = 0; i < 3; i++) { *out++ = a3[i]; }
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 4; j++) {
|
||||
a4[j] = '\0';
|
||||
}
|
||||
for (j = i; j < 4; j++) { a4[j] = '\0'; }
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
a4[j] = b64_lookup(a4[j]);
|
||||
}
|
||||
for (j = 0; j < 4; j++) { a4[j] = b64_lookup(a4[j]); }
|
||||
|
||||
a4_to_a3(a3, a4);
|
||||
|
||||
for (j = 0; j < i - 1; j++) {
|
||||
*out++ = a3[j];
|
||||
}
|
||||
for (j = 0; j < i - 1; j++) { *out++ = a3[j]; }
|
||||
}
|
||||
|
||||
return (out == (out_begin + decoded_length));
|
||||
}
|
||||
|
||||
static size_t DecodedLength(const char *in, size_t in_length) {
|
||||
static size_t DecodedLength(const char *in, size_t in_length)
|
||||
{
|
||||
int numEq = 0;
|
||||
|
||||
const char *in_end = in + in_length;
|
||||
while (*--in_end == '=')
|
||||
++numEq;
|
||||
while (*--in_end == '=') ++numEq;
|
||||
|
||||
return ((6 * in_length) / 8) - numEq;
|
||||
}
|
||||
|
||||
static size_t DecodedLength(const std::string &in) {
|
||||
static size_t DecodedLength(const std::string &in)
|
||||
{
|
||||
int numEq = 0;
|
||||
size_t n = in.size();
|
||||
|
||||
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '=';
|
||||
++it) {
|
||||
++numEq;
|
||||
}
|
||||
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) { ++numEq; }
|
||||
|
||||
return ((6 * n) / 8) - numEq;
|
||||
}
|
||||
|
||||
inline static size_t EncodedLength(size_t length) {
|
||||
return (length + 2 - ((length + 2) % 3)) / 3 * 4;
|
||||
}
|
||||
inline static size_t EncodedLength(size_t length) { return (length + 2 - ((length + 2) % 3)) / 3 * 4; }
|
||||
|
||||
inline static size_t EncodedLength(const std::string &in) {
|
||||
return EncodedLength(in.length());
|
||||
}
|
||||
inline static size_t EncodedLength(const std::string &in) { return EncodedLength(in.length()); }
|
||||
|
||||
inline static void StripPadding(std::string *in) {
|
||||
while (!in->empty() && *(in->rbegin()) == '=')
|
||||
in->resize(in->size() - 1);
|
||||
inline static void StripPadding(std::string *in)
|
||||
{
|
||||
while (!in->empty() && *(in->rbegin()) == '=') in->resize(in->size() - 1);
|
||||
}
|
||||
|
||||
private:
|
||||
static inline void a3_to_a4(unsigned char *a4, unsigned char *a3) {
|
||||
static inline void a3_to_a4(unsigned char *a4, unsigned char *a3)
|
||||
{
|
||||
a4[0] = (a3[0] & 0xfc) >> 2;
|
||||
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
|
||||
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
|
||||
a4[3] = (a3[2] & 0x3f);
|
||||
}
|
||||
|
||||
static inline void a4_to_a3(unsigned char *a3, unsigned char *a4) {
|
||||
static inline void a4_to_a3(unsigned char *a3, unsigned char *a4)
|
||||
{
|
||||
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
|
||||
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
|
||||
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
|
||||
|
@ -8,7 +8,9 @@ static constexpr auto kCharMin = std::numeric_limits<char>::min();
|
||||
static constexpr auto kCharMax = std::numeric_limits<char>::max();
|
||||
static constexpr auto kSize = kCharMax - kCharMin + 1;
|
||||
|
||||
int AsciiCodeFromCharPairSlow(char a, char b) {
|
||||
int
|
||||
AsciiCodeFromCharPairSlow(char a, char b)
|
||||
{
|
||||
a = ToLower(a);
|
||||
b = ToLower(b);
|
||||
auto ToNum = [](char x) {
|
||||
@ -23,25 +25,24 @@ int AsciiCodeFromCharPairSlow(char a, char b) {
|
||||
|
||||
int x = ToNum(a);
|
||||
int y = ToNum(b);
|
||||
if (x == -1 || y == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (x == -1 || y == -1) { return -1; }
|
||||
|
||||
return (x << 4) | y;
|
||||
}
|
||||
|
||||
std::pair<char, char> AsciiCodeToCharPairSlow(std::uint8_t value,
|
||||
bool uppercase) {
|
||||
std::pair<char, char>
|
||||
AsciiCodeToCharPairSlow(std::uint8_t value, bool uppercase)
|
||||
{
|
||||
if (uppercase) {
|
||||
return std::make_pair(kHexCharsUpper[value >> 4],
|
||||
kHexCharsUpper[value & 0x0f]);
|
||||
return std::make_pair(kHexCharsUpper[value >> 4], kHexCharsUpper[value & 0x0f]);
|
||||
} else {
|
||||
return std::make_pair(kHexCharsLower[value >> 4],
|
||||
kHexCharsLower[value & 0x0f]);
|
||||
return std::make_pair(kHexCharsLower[value >> 4], kHexCharsLower[value & 0x0f]);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<std::array<int, 256>, 256> GenAsciiCodeFromCharPairTable() {
|
||||
std::array<std::array<int, 256>, 256>
|
||||
GenAsciiCodeFromCharPairTable()
|
||||
{
|
||||
std::array<std::array<int, 256>, 256> table;
|
||||
for (char i = kCharMin; i != kCharMax; ++i) {
|
||||
for (char j = kCharMin; j != kCharMax; ++j) {
|
||||
@ -52,33 +53,40 @@ std::array<std::array<int, 256>, 256> GenAsciiCodeFromCharPairTable() {
|
||||
}
|
||||
|
||||
std::array<std::pair<char, char>, 256>
|
||||
GenAsciiCodeToCharPairTable(bool uppercase) {
|
||||
GenAsciiCodeToCharPairTable(bool uppercase)
|
||||
{
|
||||
std::array<std::pair<char, char>, 256> table;
|
||||
for (int i = 0; i != 256; ++i) {
|
||||
table[i] = AsciiCodeToCharPairSlow(i, uppercase);
|
||||
}
|
||||
for (int i = 0; i != 256; ++i) { table[i] = AsciiCodeToCharPairSlow(i, uppercase); }
|
||||
return table;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
int AsciiCodeFromCharPair(char a, char b) {
|
||||
int
|
||||
AsciiCodeFromCharPair(char a, char b)
|
||||
{
|
||||
static auto table = GenAsciiCodeFromCharPairTable();
|
||||
return table[a - kCharMin][b - kCharMin];
|
||||
}
|
||||
|
||||
inline std::pair<char, char> AsciiCodeToUpperCharPair(std::uint8_t value) {
|
||||
inline std::pair<char, char>
|
||||
AsciiCodeToUpperCharPair(std::uint8_t value)
|
||||
{
|
||||
static auto table = GenAsciiCodeToCharPairTable(true);
|
||||
return table[value];
|
||||
}
|
||||
inline std::pair<char, char> AsciiCodeToLowerCharPair(std::uint8_t value) {
|
||||
|
||||
inline std::pair<char, char>
|
||||
AsciiCodeToLowerCharPair(std::uint8_t value)
|
||||
{
|
||||
static auto table = GenAsciiCodeToCharPairTable(false);
|
||||
return table[value];
|
||||
}
|
||||
|
||||
std::pair<char, char> AsciiCodeToCharPair(std::uint8_t value, bool uppercase) {
|
||||
return uppercase ? AsciiCodeToUpperCharPair(value)
|
||||
: AsciiCodeToLowerCharPair(value);
|
||||
std::pair<char, char>
|
||||
AsciiCodeToCharPair(std::uint8_t value, bool uppercase)
|
||||
{
|
||||
return uppercase ? AsciiCodeToUpperCharPair(value) : AsciiCodeToLowerCharPair(value);
|
||||
}
|
||||
|
||||
}// namespace detail
|
||||
|
@ -23,8 +23,7 @@ static constexpr char kHexCharsUpper[] = "0123456789ABCDEF";
|
||||
|
||||
int AsciiCodeFromCharPair(char a, char b);
|
||||
|
||||
std::pair<char, char> AsciiCodeToCharPair(std::uint8_t value,
|
||||
bool uppercase = true);
|
||||
std::pair<char, char> AsciiCodeToCharPair(std::uint8_t value, bool uppercase = true);
|
||||
|
||||
}// namespace detail
|
||||
}// namespace tile
|
||||
|
@ -6,43 +6,44 @@
|
||||
|
||||
namespace tile {
|
||||
|
||||
std::string EncodeHex(Slice from, bool uppercase) {
|
||||
std::string
|
||||
EncodeHex(Slice from, bool uppercase)
|
||||
{
|
||||
std::string result;
|
||||
EncodeHex(from, &result, uppercase);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::string> DecodeHex(Slice from) {
|
||||
std::optional<std::string>
|
||||
DecodeHex(Slice from)
|
||||
{
|
||||
std::string result;
|
||||
if (!DecodeHex(from, &result)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!DecodeHex(from, &result)) { return std::nullopt; }
|
||||
return result;
|
||||
}
|
||||
|
||||
void EncodeHex(Slice from, std::string *to, bool uppercase) {
|
||||
void
|
||||
EncodeHex(Slice from, std::string *to, bool uppercase)
|
||||
{
|
||||
to->clear();
|
||||
to->reserve(from.size() * 2);
|
||||
for (auto &&e : from) {
|
||||
// auto index = static_cast<std::uint8_t>(e);
|
||||
auto pair =
|
||||
detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e), uppercase);
|
||||
auto pair = detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e), uppercase);
|
||||
to->append({pair.first, pair.second});
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeHex(Slice from, std::string *to) {
|
||||
if (from.size() % 2 != 0) {
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
DecodeHex(Slice from, std::string *to)
|
||||
{
|
||||
if (from.size() % 2 != 0) { return false; }
|
||||
|
||||
to->clear();
|
||||
to->reserve(from.size() / 2);
|
||||
for (size_t i = 0; i != from.size(); i += 2) {
|
||||
auto v = detail::AsciiCodeFromCharPair(from[i], from[i + 1]);
|
||||
if (v == -1) {
|
||||
return false;
|
||||
}
|
||||
if (v == -1) { return false; }
|
||||
|
||||
TILE_CHECK(v >= 0 && v <= 255);
|
||||
to->push_back(v);
|
||||
|
@ -7,14 +7,16 @@
|
||||
namespace tile {
|
||||
const char Hex123456FF[] = "\x12\x34\x56\xFF";
|
||||
|
||||
TEST(Hex, Default) {
|
||||
TEST(Hex, Default)
|
||||
{
|
||||
EXPECT_EQ("123456ff", EncodeHex(Hex123456FF));
|
||||
EXPECT_EQ("123456FF", EncodeHex(Hex123456FF, true));
|
||||
EXPECT_EQ(Hex123456FF, *DecodeHex("123456ff"));
|
||||
EXPECT_EQ(Hex123456FF, *DecodeHex("123456FF"));
|
||||
}
|
||||
|
||||
TEST(Hex, Random) {
|
||||
TEST(Hex, Random)
|
||||
{
|
||||
std::string random_str;
|
||||
std::string encoded;
|
||||
std::string decoded;
|
||||
|
@ -7,15 +7,13 @@ namespace tile {
|
||||
|
||||
// [0-9a-zA-Z]
|
||||
namespace {
|
||||
std::array<bool, 256> GenerateUnescapedCharBitmap(Slice unescaped_chars) {
|
||||
std::array<bool, 256>
|
||||
GenerateUnescapedCharBitmap(Slice unescaped_chars)
|
||||
{
|
||||
std::array<bool, 256> result{};
|
||||
for (auto &&e : unescaped_chars) {
|
||||
result[e] = true;
|
||||
}
|
||||
for (auto &&e : unescaped_chars) { result[e] = true; }
|
||||
|
||||
for (int i = 0; i != 10; ++i) {
|
||||
result[i + '0'] = true;
|
||||
}
|
||||
for (int i = 0; i != 10; ++i) { result[i + '0'] = true; }
|
||||
|
||||
for (int i = 0; i != 26; ++i) {
|
||||
result[i + 'A'] = true;
|
||||
@ -26,52 +24,50 @@ std::array<bool, 256> GenerateUnescapedCharBitmap(Slice unescaped_chars) {
|
||||
}
|
||||
|
||||
const std::array<std::array<bool, 256>, 2> &
|
||||
GetUnescapedCharBitmap(const PercentEncodingStyle &style) {
|
||||
GetUnescapedCharBitmap(const PercentEncodingStyle &style)
|
||||
{
|
||||
static const std::array<std::array<bool, 256>, 2> kUnescapedChars[] = {
|
||||
/* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"),
|
||||
GenerateUnescapedCharBitmap("_-!.*~'()")},
|
||||
/* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"), GenerateUnescapedCharBitmap("_-!.*~'()") },
|
||||
|
||||
/* rfc3986 = */
|
||||
{GenerateUnescapedCharBitmap("_-,;:!?.'()[]@*/&#+=~$"),
|
||||
GenerateUnescapedCharBitmap("_-.~")
|
||||
{GenerateUnescapedCharBitmap("_-,;:!?.'()[]@*/&#+=~$"), GenerateUnescapedCharBitmap("_-.~")
|
||||
|
||||
},
|
||||
/* rfc5987 = */
|
||||
{GenerateUnescapedCharBitmap("!#$&+-.^_`|~"),
|
||||
GenerateUnescapedCharBitmap("!#$&+-.^_`|~")}};
|
||||
{GenerateUnescapedCharBitmap("!#$&+-.^_`|~"), GenerateUnescapedCharBitmap("!#$&+-.^_`|~")}
|
||||
};
|
||||
|
||||
return kUnescapedChars[static_cast<int>(style)];
|
||||
}
|
||||
}// namespace
|
||||
|
||||
PercentEncodingOptions::PercentEncodingOptions(PercentEncodingStyle s, bool er)
|
||||
: style(s), escape_reserved(er) {}
|
||||
PercentEncodingOptions::PercentEncodingOptions(PercentEncodingStyle s, bool er) : style(s), escape_reserved(er) {}
|
||||
|
||||
std::string EncodePercent(Slice from, const PercentEncodingOptions &options) {
|
||||
std::string
|
||||
EncodePercent(Slice from, const PercentEncodingOptions &options)
|
||||
{
|
||||
std::string result;
|
||||
EncodePercent(from, &result, options);
|
||||
return result;
|
||||
}
|
||||
std::optional<std::string> DecodePercent(Slice from,
|
||||
bool decode_plus_sign_as_whitespace) {
|
||||
|
||||
std::optional<std::string>
|
||||
DecodePercent(Slice from, bool decode_plus_sign_as_whitespace)
|
||||
{
|
||||
std::string result;
|
||||
if (DecodePercent(from, &result, decode_plus_sign_as_whitespace)) {
|
||||
return result;
|
||||
}
|
||||
if (DecodePercent(from, &result, decode_plus_sign_as_whitespace)) { return result; }
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void EncodePercent(Slice from, std::string *to,
|
||||
const PercentEncodingOptions &options) {
|
||||
void
|
||||
EncodePercent(Slice from, std::string *to, const PercentEncodingOptions &options)
|
||||
{
|
||||
|
||||
auto &&unescaped =
|
||||
GetUnescapedCharBitmap(options.style)[options.escape_reserved];
|
||||
auto &&unescaped = GetUnescapedCharBitmap(options.style)[options.escape_reserved];
|
||||
|
||||
int escape_char_count = 0;
|
||||
for (auto &&e : from) {
|
||||
if (!unescaped[static_cast<std::uint8_t>(e)]) {
|
||||
++escape_char_count;
|
||||
}
|
||||
if (!unescaped[static_cast<std::uint8_t>(e)]) { ++escape_char_count; }
|
||||
}
|
||||
|
||||
to->clear();
|
||||
@ -87,15 +83,14 @@ void EncodePercent(Slice from, std::string *to,
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodePercent(Slice from, std::string *to,
|
||||
bool decode_plus_sign_as_whitespace) {
|
||||
bool
|
||||
DecodePercent(Slice from, std::string *to, bool decode_plus_sign_as_whitespace)
|
||||
{
|
||||
to->clear();
|
||||
to->reserve(from.size());
|
||||
for (auto iter = from.begin(); iter != from.end();) {
|
||||
if (*iter == '%') {
|
||||
if (iter + 3 > from.end()) {
|
||||
return false;
|
||||
}
|
||||
if (iter + 3 > from.end()) { return false; }
|
||||
auto v = detail::AsciiCodeFromCharPair(*(iter + 1), *(iter + 2));
|
||||
if (TILE_LIKELY(v != -1)) {
|
||||
to->push_back(v);
|
||||
|
@ -18,22 +18,18 @@ struct PercentEncodingOptions {
|
||||
PercentEncodingStyle style = PercentEncodingStyle::Rfc3986;
|
||||
bool escape_reserved = true;
|
||||
|
||||
PercentEncodingOptions(PercentEncodingStyle s = PercentEncodingStyle::Rfc3986,
|
||||
bool escape_reserved = true);
|
||||
PercentEncodingOptions(PercentEncodingStyle s = PercentEncodingStyle::Rfc3986, bool escape_reserved = true);
|
||||
};
|
||||
|
||||
std::string
|
||||
EncodePercent(Slice from,
|
||||
const PercentEncodingOptions &options =
|
||||
internal::EarlyInitConstant<PercentEncodingOptions>());
|
||||
std::optional<std::string>
|
||||
DecodePercent(Slice from, bool decode_plus_sign_as_whitespace = false);
|
||||
const PercentEncodingOptions &options = internal::EarlyInitConstant<PercentEncodingOptions>());
|
||||
std::optional<std::string> DecodePercent(Slice from, bool decode_plus_sign_as_whitespace = false);
|
||||
|
||||
void EncodePercent(Slice from, std::string *to,
|
||||
const PercentEncodingOptions &options =
|
||||
internal::EarlyInitConstant<PercentEncodingOptions>());
|
||||
bool DecodePercent(Slice from, std::string *to,
|
||||
bool decode_plus_sign_as_whitespace = false);
|
||||
void EncodePercent(Slice from,
|
||||
std::string *to,
|
||||
const PercentEncodingOptions &options = internal::EarlyInitConstant<PercentEncodingOptions>());
|
||||
bool DecodePercent(Slice from, std::string *to, bool decode_plus_sign_as_whitespace = false);
|
||||
|
||||
}// namespace tile
|
||||
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
namespace tile {
|
||||
|
||||
TEST(PercentEncoding, Emca262) {
|
||||
TEST(PercentEncoding, Emca262)
|
||||
{
|
||||
// Shamelessly copied from:
|
||||
//
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
|
||||
@ -16,11 +17,9 @@ TEST(PercentEncoding, Emca262) {
|
||||
Slice set4 = "ABC abc 123";// Alphanumeric Characters + Space
|
||||
|
||||
// Reserved chars are escaped.
|
||||
const PercentEncodingOptions ecma262_reversed(PercentEncodingStyle::Ecma262,
|
||||
true);
|
||||
const PercentEncodingOptions ecma262_reversed(PercentEncodingStyle::Ecma262, true);
|
||||
|
||||
EXPECT_EQ("%3B%2C%2F%3F%3A%40%26%3D%2B%24",
|
||||
EncodePercent(set1, ecma262_reversed));
|
||||
EXPECT_EQ("%3B%2C%2F%3F%3A%40%26%3D%2B%24", EncodePercent(set1, ecma262_reversed));
|
||||
EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262_reversed));
|
||||
EXPECT_EQ("%23", EncodePercent(set3, ecma262_reversed));
|
||||
EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, ecma262_reversed));
|
||||
@ -43,25 +42,24 @@ TEST(PercentEncoding, Emca262) {
|
||||
EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123"));
|
||||
}
|
||||
|
||||
TEST(PercentEncoding, Rfc3986) {
|
||||
TEST(PercentEncoding, Rfc3986)
|
||||
{
|
||||
Slice str = "_-,;:!?.'()[]@*/&#+=~$ABC abc 123";
|
||||
|
||||
const PercentEncodingOptions rfc3986_reversed(PercentEncodingStyle::Rfc3986,
|
||||
true);
|
||||
const PercentEncodingOptions rfc3986_reversed(PercentEncodingStyle::Rfc3986, true);
|
||||
const PercentEncodingOptions rfc3986(PercentEncodingStyle::Rfc3986, false);
|
||||
EXPECT_EQ(
|
||||
"_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%2B%3D~%24ABC%20abc%"
|
||||
EXPECT_EQ("_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%2B%3D~%24ABC%20abc%"
|
||||
"20123",
|
||||
EncodePercent(str, rfc3986_reversed));
|
||||
EXPECT_EQ("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123",
|
||||
EncodePercent(str, rfc3986));
|
||||
EXPECT_EQ("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123", EncodePercent(str, rfc3986));
|
||||
EXPECT_EQ(str,
|
||||
DecodePercent("_-%2C%3B%3A%21%3F.%27%28%29%5B%5D%40%2A%2F%26%23%"
|
||||
"2B%3D~%24ABC%20abc%20123"));
|
||||
EXPECT_EQ(str, DecodePercent("_-,;:!?.'()[]@*/&#+=~$ABC%20abc%20123"));
|
||||
}
|
||||
|
||||
TEST(PercentEncoding, Rfc5987) {
|
||||
TEST(PercentEncoding, Rfc5987)
|
||||
{
|
||||
// `encodeRFC5987ValueChars from MDN does not seems quite right (in that it
|
||||
// does escape `#` `$` ..., which is not required.):
|
||||
//
|
||||
@ -87,20 +85,16 @@ TEST(PercentEncoding, Rfc5987) {
|
||||
// PercentEncodingStyle::Rfc5987}));
|
||||
|
||||
const PercentEncodingOptions rfc5987(PercentEncodingStyle::Rfc5987, false);
|
||||
const PercentEncodingOptions rfc5987_reversed(PercentEncodingStyle::Rfc5987,
|
||||
true);
|
||||
const PercentEncodingOptions rfc5987_reversed(PercentEncodingStyle::Rfc5987, true);
|
||||
|
||||
Slice str = "!123'-!#$&()*,./:;?@[]^_`|~+=ABC abc";
|
||||
|
||||
EXPECT_EQ("!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc",
|
||||
EncodePercent(str, rfc5987));
|
||||
EXPECT_EQ(
|
||||
str,
|
||||
DecodePercent(
|
||||
"!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc"));
|
||||
EXPECT_EQ("!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc", EncodePercent(str, rfc5987));
|
||||
EXPECT_EQ(str, DecodePercent("!123%27-!#$&%28%29%2A%2C.%2F%3A%3B%3F%40%5B%5D^_`|~+%3DABC%20abc"));
|
||||
}
|
||||
|
||||
TEST(PercentEncoding, DecodePlusSignAsWhitespace) {
|
||||
TEST(PercentEncoding, DecodePlusSignAsWhitespace)
|
||||
{
|
||||
EXPECT_EQ("a+b", DecodePercent("a+b"));
|
||||
EXPECT_EQ("a b", DecodePercent("a+b", true));
|
||||
}
|
||||
|
@ -7,7 +7,9 @@
|
||||
|
||||
namespace tile {
|
||||
|
||||
void Benchmark_Base64Encode(benchmark::State &state) {
|
||||
void
|
||||
Benchmark_Base64Encode(benchmark::State &state)
|
||||
{
|
||||
std::string random_str;
|
||||
std::string out;
|
||||
while (state.KeepRunning()) {
|
||||
@ -18,7 +20,10 @@ void Benchmark_Base64Encode(benchmark::State &state) {
|
||||
EncodeBase64(random_str, &out);
|
||||
}
|
||||
}
|
||||
void Benchmark_Base64Decode(benchmark::State &state) {
|
||||
|
||||
void
|
||||
Benchmark_Base64Decode(benchmark::State &state)
|
||||
{
|
||||
std::string random_str;
|
||||
std::string base64;
|
||||
std::string plain_text;
|
||||
@ -32,7 +37,9 @@ void Benchmark_Base64Decode(benchmark::State &state) {
|
||||
}
|
||||
}
|
||||
|
||||
void Benchmark_PercentEncode(benchmark::State &state) {
|
||||
void
|
||||
Benchmark_PercentEncode(benchmark::State &state)
|
||||
{
|
||||
std::string random_str;
|
||||
std::string out;
|
||||
while (state.KeepRunning()) {
|
||||
@ -43,7 +50,9 @@ void Benchmark_PercentEncode(benchmark::State &state) {
|
||||
}
|
||||
}
|
||||
|
||||
void Benchmark_PercentDecode(benchmark::State &state) {
|
||||
void
|
||||
Benchmark_PercentDecode(benchmark::State &state)
|
||||
{
|
||||
std::string random_str;
|
||||
std::string percent;
|
||||
std::string plain_text;
|
||||
@ -56,7 +65,9 @@ void Benchmark_PercentDecode(benchmark::State &state) {
|
||||
}
|
||||
}
|
||||
|
||||
void Benchmark_HexEncode(benchmark::State &state) {
|
||||
void
|
||||
Benchmark_HexEncode(benchmark::State &state)
|
||||
{
|
||||
std::string random_str;
|
||||
std::string percent;
|
||||
while (state.KeepRunning()) {
|
||||
@ -67,7 +78,9 @@ void Benchmark_HexEncode(benchmark::State &state) {
|
||||
}
|
||||
}
|
||||
|
||||
void Benchmark_HexDecode(benchmark::State &state) {
|
||||
void
|
||||
Benchmark_HexDecode(benchmark::State &state)
|
||||
{
|
||||
std::string random_str;
|
||||
std::string percent;
|
||||
std::string plain_text;
|
||||
|
@ -7,19 +7,19 @@
|
||||
|
||||
namespace tile {
|
||||
template<class T, class = enable_if_t<std::is_enum<T>::value>>
|
||||
constexpr auto underlying_value(T v) -> typename std::underlying_type<T>::type {
|
||||
constexpr auto
|
||||
underlying_value(T v) -> typename std::underlying_type<T>::type
|
||||
{
|
||||
return static_cast<typename std::underlying_type<T>::type>(v);
|
||||
}
|
||||
|
||||
#define TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, Op) \
|
||||
constexpr Type operator Op(Type left, Type right) { \
|
||||
return static_cast<Type>(::tile::underlying_value(left) \
|
||||
Op ::tile::underlying_value(right)); \
|
||||
constexpr Type operator Op(Type left, Type right) \
|
||||
{ \
|
||||
return static_cast<Type>(::tile::underlying_value(left) Op ::tile::underlying_value(right)); \
|
||||
}
|
||||
#define TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, Op) \
|
||||
constexpr Type operator Op(Type v) { \
|
||||
return static_cast<Type>(Op ::tile::underlying_value(v)); \
|
||||
}
|
||||
constexpr Type operator Op(Type v) { return static_cast<Type>(Op ::tile::underlying_value(v)); }
|
||||
#define TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, Op) \
|
||||
constexpr Type &operator Op(Type & left, Type right) { return left Op right; }
|
||||
|
||||
@ -31,9 +31,7 @@ constexpr auto underlying_value(T v) -> typename std::underlying_type<T>::type {
|
||||
TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, &=) \
|
||||
TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, ^=) \
|
||||
TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, ~) \
|
||||
constexpr bool operator!(Type value) { \
|
||||
return !::tile::underlying_value(value); \
|
||||
}
|
||||
constexpr bool operator!(Type value) { return !::tile::underlying_value(value); }
|
||||
|
||||
}// namespace tile
|
||||
|
||||
|
@ -13,46 +13,57 @@ namespace tile {
|
||||
class ErasedPtr final {
|
||||
public:
|
||||
using Deleter = void (*)(void *);
|
||||
constexpr ErasedPtr(std::nullptr_t = nullptr)
|
||||
: ptr_(nullptr), deleter_(nullptr) {}
|
||||
|
||||
constexpr ErasedPtr(std::nullptr_t = nullptr) : ptr_(nullptr), deleter_(nullptr) {}
|
||||
|
||||
template<typename T>
|
||||
constexpr ErasedPtr(T *ptr) noexcept
|
||||
: ptr_(ptr), deleter_([](void *ptr) { delete static_cast<T *>(ptr); }) {}
|
||||
: ptr_(ptr),
|
||||
deleter_([](void *ptr) { delete static_cast<T *>(ptr); })
|
||||
{}
|
||||
|
||||
template<typename T, typename D>
|
||||
constexpr ErasedPtr(T *ptr, D deleter) noexcept
|
||||
: ptr_(ptr), deleter_(deleter) {}
|
||||
ErasedPtr(ErasedPtr &&ptr) noexcept : ptr_(ptr.ptr_), deleter_(ptr.deleter_) {
|
||||
ptr.ptr_ = nullptr;
|
||||
}
|
||||
: ptr_(ptr),
|
||||
deleter_(deleter)
|
||||
{}
|
||||
|
||||
ErasedPtr &operator=(ErasedPtr &&ptr) noexcept {
|
||||
if (TILE_LIKELY(this != &ptr)) {
|
||||
Reset();
|
||||
}
|
||||
ErasedPtr(ErasedPtr &&ptr) noexcept : ptr_(ptr.ptr_), deleter_(ptr.deleter_) { ptr.ptr_ = nullptr; }
|
||||
|
||||
ErasedPtr &operator=(ErasedPtr &&ptr) noexcept
|
||||
{
|
||||
if (TILE_LIKELY(this != &ptr)) { Reset(); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
ErasedPtr(const ErasedPtr &) = delete;
|
||||
ErasedPtr &operator=(const ErasedPtr &) = delete;
|
||||
|
||||
~ErasedPtr() {
|
||||
if (ptr_) {
|
||||
deleter_(ptr_);
|
||||
}
|
||||
~ErasedPtr()
|
||||
{
|
||||
if (ptr_) { deleter_(ptr_); }
|
||||
}
|
||||
|
||||
constexpr void *Get() const noexcept { return ptr_; }
|
||||
template <typename T> T *UncheckedGet() const noexcept {
|
||||
|
||||
template<typename T>
|
||||
T *UncheckedGet() const noexcept
|
||||
{
|
||||
return static_cast<T *>(ptr_);
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const noexcept { return !!ptr_; }
|
||||
void Reset(std::nullptr_t = nullptr) noexcept {
|
||||
|
||||
void Reset(std::nullptr_t = nullptr) noexcept
|
||||
{
|
||||
if (ptr_) {
|
||||
deleter_(ptr_);
|
||||
ptr_ = nullptr;
|
||||
}
|
||||
}
|
||||
TILE_NODISCARD void *Leak() noexcept {
|
||||
|
||||
TILE_NODISCARD void *Leak() noexcept
|
||||
{
|
||||
void *rc = nullptr;
|
||||
std::swap(rc, ptr_);
|
||||
return rc;
|
||||
|
2001
tile/base/expected.h
2001
tile/base/expected.h
File diff suppressed because it is too large
Load Diff
@ -9,22 +9,20 @@ namespace tile {
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
TILE_CHECK((path).empty() || (path).front() == '/', "Invalid path: [{}]", path);
|
||||
|
||||
std::pair<Slice, Slice> SplitFirstPart(Slice path) {
|
||||
std::pair<Slice, Slice>
|
||||
SplitFirstPart(Slice path)
|
||||
{
|
||||
auto pos = path.find_first_of('/');
|
||||
if (pos == tile::Slice::npos) {
|
||||
return std::make_pair(path, "");
|
||||
@ -33,44 +31,44 @@ std::pair<Slice, Slice> SplitFirstPart(Slice path) {
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Slice, Slice> SplitLastPart(Slice path) {
|
||||
std::pair<Slice, Slice>
|
||||
SplitLastPart(Slice path)
|
||||
{
|
||||
auto pos = path.find_last_of('/');
|
||||
if (pos == tile::Slice::npos) {
|
||||
return std::make_pair("", path);
|
||||
}
|
||||
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);
|
||||
}
|
||||
std::string
|
||||
JoinPath(Slice a, Slice b)
|
||||
{
|
||||
if (EndsWith(b, "/")) { b.RemoveSuffix(1); }
|
||||
|
||||
if (EndsWith(a, "/")) {
|
||||
a.RemoveSuffix(1);
|
||||
}
|
||||
if (EndsWith(a, "/")) { a.RemoveSuffix(1); }
|
||||
|
||||
if (b.empty()) {
|
||||
return a.ToString();
|
||||
}
|
||||
if (a.empty()) {
|
||||
return b.ToString();
|
||||
}
|
||||
if (b.empty()) { return a.ToString(); }
|
||||
if (a.empty()) { return b.ToString(); }
|
||||
|
||||
return a.ToString() + "/" + b.ToString();
|
||||
}
|
||||
|
||||
std::string SubstituteEscapedSlashForZero(Slice path) {
|
||||
std::string
|
||||
SubstituteEscapedSlashForZero(Slice path)
|
||||
{
|
||||
TILE_CHECK(path.find('\0') == tile::Slice::npos);
|
||||
return Replace(path, "\\/", '\0');
|
||||
}
|
||||
|
||||
std::string SubstituteZeroForEscapedSlash(Slice path) {
|
||||
std::string
|
||||
SubstituteZeroForEscapedSlash(Slice path)
|
||||
{
|
||||
return Replace(path, '\0', "\\/");
|
||||
}
|
||||
|
||||
std::string UnescapeZeroToPlainSlash(Slice path) {
|
||||
std::string
|
||||
UnescapeZeroToPlainSlash(Slice path)
|
||||
{
|
||||
return Replace(path, '\0', '/');
|
||||
}
|
||||
|
||||
@ -78,7 +76,8 @@ std::string UnescapeZeroToPlainSlash(Slice path) {
|
||||
|
||||
// } // namespace exposed_var
|
||||
ExposedVarGroup::Handle
|
||||
ExposedVarGroup::Add(Slice rel_path, std::function<Json::Value()> value) {
|
||||
ExposedVarGroup::Add(Slice rel_path, std::function<Json::Value()> value)
|
||||
{
|
||||
|
||||
auto real_path = SubstituteEscapedSlashForZero(rel_path);
|
||||
CHECK_RELATIVE_PATH(rel_path);
|
||||
@ -88,13 +87,9 @@ ExposedVarGroup::Add(Slice rel_path, std::function<Json::Value()> value) {
|
||||
|
||||
auto moved_func = tile::MakeMoveOnCopy(value);
|
||||
return CreateUpto(path_and_name.first)
|
||||
->AddDirect(
|
||||
name,
|
||||
[name, moved_func](Slice expected) -> std::optional<Json::Value> {
|
||||
->AddDirect(name, [name, moved_func](Slice expected) -> std::optional<Json::Value> {
|
||||
auto jsv = moved_func.Ref()();
|
||||
if (expected.empty()) {
|
||||
return jsv;
|
||||
}
|
||||
if (expected.empty()) { return jsv; }
|
||||
|
||||
auto real_path = SubstituteEscapedSlashForZero(expected);
|
||||
Json::Value *ptr = &jsv;
|
||||
@ -125,41 +120,42 @@ ExposedVarGroup::Add(Slice rel_path, std::function<Json::Value()> value) {
|
||||
}
|
||||
|
||||
ExposedVarGroup::Handle
|
||||
ExposedVarGroup::Add(Slice rel_path, ExposedVarDynamicTree *dynamic_tree) {
|
||||
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 CreateUpto(path_and_name.first)->AddDirect(name, [dynamic_tree](Slice inner_path) {
|
||||
return dynamic_tree->TryGet(inner_path);
|
||||
});
|
||||
}
|
||||
|
||||
ExposedVarGroup *ExposedVarGroup::FindOrCreate(Slice abs_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) {
|
||||
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
if (name_and_rest.first.empty()) { return parent->Dump(); }
|
||||
|
||||
auto s = name_and_rest.first.ToString();
|
||||
std::lock_guard<std::mutex> lk(parent->lock_);
|
||||
@ -176,18 +172,27 @@ std::optional<Json::Value> ExposedVarGroup::TryGet(Slice abs_path) {
|
||||
}
|
||||
}
|
||||
|
||||
ExposedVarGroup::ExposedVarGroup(std::string abs_path)
|
||||
: abs_path_(std::move(abs_path)) {
|
||||
ExposedVarGroup::ExposedVarGroup(std::string abs_path) : abs_path_(std::move(abs_path))
|
||||
{
|
||||
CHECK_ABSOLUTE_PATH(abs_path);
|
||||
}
|
||||
|
||||
ExposedVarGroup *ExposedVarGroup::Root() {
|
||||
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) {
|
||||
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;
|
||||
|
||||
@ -205,13 +210,13 @@ ExposedVarGroup *ExposedVarGroup::FindLowest(Slice rel_path, Slice *left) {
|
||||
rel_path = name_and_rest.second;
|
||||
}
|
||||
|
||||
if (left) {
|
||||
*left = rel_path;
|
||||
}
|
||||
if (left) { *left = rel_path; }
|
||||
return current;
|
||||
}
|
||||
|
||||
ExposedVarGroup *ExposedVarGroup::CreateUpto(Slice rel_path) {
|
||||
ExposedVarGroup *
|
||||
ExposedVarGroup::CreateUpto(Slice rel_path)
|
||||
{
|
||||
CHECK_RELATIVE_PATH(rel_path);
|
||||
|
||||
Slice left_path;
|
||||
@ -225,10 +230,8 @@ ExposedVarGroup *ExposedVarGroup::CreateUpto(Slice rel_path) {
|
||||
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->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));
|
||||
@ -236,20 +239,19 @@ ExposedVarGroup *ExposedVarGroup::CreateUpto(Slice rel_path) {
|
||||
current = &*(current->nodes_[s]);
|
||||
}
|
||||
|
||||
TILE_CHECK(EndsWith(current->AbsolutePath(), rel_path), "[{}] vs [{}]",
|
||||
current->AbsolutePath(), rel_path);
|
||||
TILE_CHECK(EndsWith(current->AbsolutePath(), rel_path), "[{}] vs [{}]", current->AbsolutePath(), rel_path);
|
||||
return current;
|
||||
}
|
||||
|
||||
ExposedVarGroup::Handle ExposedVarGroup::AddDirect(Slice name, Getter value) {
|
||||
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,
|
||||
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));
|
||||
TILE_CHECK(nodes_.find(s) == nodes_.end(), "Path [{}] has already been used.", JoinPath(AbsolutePath(), name));
|
||||
leaves_[s] = std::move(value);
|
||||
|
||||
return Deferred([this, s] {
|
||||
@ -258,28 +260,29 @@ ExposedVarGroup::Handle ExposedVarGroup::AddDirect(Slice name, Getter value) {
|
||||
});
|
||||
}
|
||||
|
||||
Json::Value ExposedVarGroup::Dump() const {
|
||||
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 &&node : nodes_) { jsv[UnescapeZeroToPlainSlash(node.first)] = node.second->Dump(); }
|
||||
|
||||
for (auto &&leave : leaves_) {
|
||||
jsv[UnescapeZeroToPlainSlash(leave.first)] = *leave.second("");
|
||||
}
|
||||
for (auto &&leave : leaves_) { jsv[UnescapeZeroToPlainSlash(leave.first)] = *leave.second(""); }
|
||||
|
||||
return jsv;
|
||||
}
|
||||
|
||||
ExposedVarDynamicTree::ExposedVarDynamicTree(
|
||||
Slice rel_path, std::function<Json::Value()> getter,
|
||||
ExposedVarDynamicTree::ExposedVarDynamicTree(Slice rel_path,
|
||||
std::function<Json::Value()> getter,
|
||||
ExposedVarGroup *parent)
|
||||
: getter_(std::move(getter)) {
|
||||
: getter_(std::move(getter))
|
||||
{
|
||||
handle_ = parent->Add(rel_path, this);
|
||||
}
|
||||
|
||||
std::optional<Json::Value> ExposedVarDynamicTree::TryGet(Slice rel_path) const {
|
||||
std::optional<Json::Value>
|
||||
ExposedVarDynamicTree::TryGet(Slice rel_path) const
|
||||
{
|
||||
auto real_path = SubstituteEscapedSlashForZero(rel_path);
|
||||
auto jsv = getter_();
|
||||
Json::Value *ptr = &jsv;
|
||||
@ -290,9 +293,7 @@ std::optional<Json::Value> ExposedVarDynamicTree::TryGet(Slice rel_path) const {
|
||||
ptr = &(*ptr)[unescaped];
|
||||
}
|
||||
|
||||
if (ptr->isNull()) {
|
||||
return {};
|
||||
}
|
||||
if (ptr->isNull()) { return {}; }
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
@ -16,49 +16,50 @@ namespace tile {
|
||||
namespace exposed_var {
|
||||
|
||||
template<typename T>
|
||||
auto ToJsonValue(const T &t)
|
||||
-> enable_if_t<std::is_same<T, Json::Value>::value, Json::Value> {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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_integral<T>::value && !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 &&
|
||||
std::is_same<std::string,
|
||||
decltype(format_as(std::declval<T>()))>::value,
|
||||
Json::Value> {
|
||||
auto
|
||||
ToJsonValue(const T &t) -> enable_if_t<!std::is_integral<T>::value && !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
|
||||
&& 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) {
|
||||
template<class T>
|
||||
Json::Value
|
||||
ToJsonValue(const std::atomic<T> &v)
|
||||
{
|
||||
return ToJsonValue(v.load(std::memory_order_relaxed));
|
||||
}
|
||||
}// namespace exposed_var
|
||||
@ -94,26 +95,26 @@ private:
|
||||
std::unordered_map<std::string, Getter> leaves_;
|
||||
};
|
||||
|
||||
template <typename T> class ExposedVar {
|
||||
template<typename T>
|
||||
class ExposedVar {
|
||||
public:
|
||||
ExposedVar(Slice rel_path) {
|
||||
LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/"));
|
||||
}
|
||||
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)) {
|
||||
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_); });
|
||||
void LinkToParent(Slice rel_path, ExposedVarGroup *parent)
|
||||
{
|
||||
handle_ = parent->Add(rel_path, [this] { return exposed_var::ToJsonValue(obj_); });
|
||||
}
|
||||
|
||||
private:
|
||||
@ -121,14 +122,15 @@ private:
|
||||
ExposedVarGroup::Handle handle_;
|
||||
};
|
||||
|
||||
template <typename T> class ExposedVarDynamic {
|
||||
template<typename T>
|
||||
class ExposedVarDynamic {
|
||||
public:
|
||||
ExposedVarDynamic(
|
||||
Slice rel_path, std::function<T()> getter,
|
||||
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_()); });
|
||||
: getter_(std::move(getter))
|
||||
{
|
||||
handle_ = parent->Add(rel_path, [this] { return exposed_var::ToJsonValue(getter_()); });
|
||||
}
|
||||
|
||||
private:
|
||||
@ -138,8 +140,8 @@ private:
|
||||
|
||||
class ExposedVarDynamicTree {
|
||||
public:
|
||||
ExposedVarDynamicTree(
|
||||
Slice rel_path, std::function<Json::Value()> getter,
|
||||
ExposedVarDynamicTree(Slice rel_path,
|
||||
std::function<Json::Value()> getter,
|
||||
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"));
|
||||
|
||||
std::optional<Json::Value> TryGet(Slice rel_path) const;
|
||||
@ -150,10 +152,11 @@ private:
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename T> struct IdentityTime {
|
||||
std::uint64_t operator()(const T &val) const {
|
||||
return (ReadSteadyClock() - std::chrono::steady_clock::time_point()) /
|
||||
std::chrono::nanoseconds(1);
|
||||
template<typename T>
|
||||
struct IdentityTime {
|
||||
std::uint64_t operator()(const T &val) const
|
||||
{
|
||||
return (ReadSteadyClock() - std::chrono::steady_clock::time_point()) / std::chrono::nanoseconds(1);
|
||||
}
|
||||
};
|
||||
}// namespace detail
|
||||
@ -161,19 +164,20 @@ template <typename T> struct IdentityTime {
|
||||
template<typename T, typename F = detail::IdentityTime<T>>
|
||||
class ExposedMetrics {
|
||||
public:
|
||||
explicit ExposedMetrics(Slice rel_path) {
|
||||
LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/"));
|
||||
}
|
||||
explicit ExposedMetrics(Slice rel_path) { LinkToParent(rel_path, ExposedVarGroup::FindOrCreate("/")); }
|
||||
|
||||
private:
|
||||
Json::Value ToJsonValue(const T &v) {
|
||||
Json::Value ToJsonValue(const T &v)
|
||||
{
|
||||
Json::Value result;
|
||||
std::unordered_map<std::string, T> m = {{"1s", 1}};
|
||||
for (auto &&item : m) {
|
||||
}
|
||||
std::unordered_map<std::string, T> m = {
|
||||
{"1s", 1}
|
||||
};
|
||||
for (auto &&item : m) {}
|
||||
}
|
||||
|
||||
void LinkToParent(Slice rel_path, ExposedVarGroup *parent) {
|
||||
void LinkToParent(Slice rel_path, ExposedVarGroup *parent)
|
||||
{
|
||||
handle_ = parent->Add(rel_path, [this] { return ToJsonValue(obj_); });
|
||||
}
|
||||
|
||||
@ -182,11 +186,16 @@ private:
|
||||
ExposedVarGroup::Handle handle_;
|
||||
};
|
||||
|
||||
template <typename T> using ExposedCounter = ExposedVar<T>;
|
||||
template <typename T> using ExposedGauge = ExposedVar<T>;
|
||||
template <typename T> using ExposedMiner = ExposedVar<T>;
|
||||
template <typename T> using ExposedMaxer = ExposedVar<T>;
|
||||
template <typename T> using ExposedAverager = ExposedVar<T>;
|
||||
template<typename T>
|
||||
using ExposedCounter = ExposedVar<T>;
|
||||
template<typename T>
|
||||
using ExposedGauge = ExposedVar<T>;
|
||||
template<typename T>
|
||||
using ExposedMiner = ExposedVar<T>;
|
||||
template<typename T>
|
||||
using ExposedMaxer = ExposedVar<T>;
|
||||
template<typename T>
|
||||
using ExposedAverager = ExposedVar<T>;
|
||||
|
||||
}// namespace tile
|
||||
|
||||
|
@ -4,10 +4,15 @@
|
||||
#include "json/json.h"
|
||||
|
||||
namespace tile {
|
||||
ExposedVarGroup *GetFancyGroup() {
|
||||
ExposedVarGroup *
|
||||
GetFancyGroup()
|
||||
{
|
||||
return ExposedVarGroup::FindOrCreate("/a/b");
|
||||
}
|
||||
Json::Value GetTree() {
|
||||
|
||||
Json::Value
|
||||
GetTree()
|
||||
{
|
||||
Json::Value jsv;
|
||||
jsv["dir"]["sub-dir"]["key"] = 5;
|
||||
jsv["key"] = "6";
|
||||
@ -21,10 +26,10 @@ ExposedVar<double> f1("f1", 6.2, ExposedVarGroup::FindOrCreate("/x/y/z"));
|
||||
auto GreatGroup = ExposedVarGroup::FindOrCreate("/a/b");
|
||||
|
||||
// `/a/b/ds1`
|
||||
ExposedVarDynamic<std::string>
|
||||
ds1("ds1", [] { return "test_str"; }, GetFancyGroup());
|
||||
ExposedVarDynamic<std::string> ds1("ds1", [] { return "test_str"; }, GetFancyGroup());
|
||||
|
||||
TEST(ExposedVar, Mutate) {
|
||||
TEST(ExposedVar, Mutate)
|
||||
{
|
||||
auto opt = ExposedVarGroup::TryGet("/");
|
||||
ASSERT_TRUE(opt);
|
||||
auto &&jsv = *opt;
|
||||
|
@ -11,36 +11,40 @@
|
||||
namespace tile {
|
||||
namespace future {
|
||||
template<typename... Ts>
|
||||
struct are_rvalue_refs
|
||||
: internal::conjunction<std::is_rvalue_reference<Ts>...> {};
|
||||
struct are_rvalue_refs : internal::conjunction<std::is_rvalue_reference<Ts>...> {};
|
||||
|
||||
static_assert(are_rvalue_refs<int &&, char &&>::value, "");
|
||||
static_assert(!are_rvalue_refs<int, char &&>::value, "");
|
||||
|
||||
// Rebinds `Ts...` in `TT<....>` to `UT<...>`
|
||||
// Example:
|
||||
// rebind_t<Future<int, double>, Promise<>> -> Promise<int, double>
|
||||
template <typename T, template <typename...> class To> struct rebind;
|
||||
template <template <typename...> class From, typename... Ts,
|
||||
template <typename...> class To>
|
||||
template<typename T, template<typename...> class To>
|
||||
struct rebind;
|
||||
|
||||
template<template<typename...> class From, typename... Ts, template<typename...> class To>
|
||||
struct rebind<From<Ts...>, To> {
|
||||
using type = To<Ts...>;
|
||||
};
|
||||
template<typename T, template<typename...> class To>
|
||||
using rebind_t = typename rebind<T, To>::type;
|
||||
|
||||
static_assert(std::is_same<rebind_t<std::common_type<>, Types>, Types<>>::value,
|
||||
"");
|
||||
static_assert(std::is_same<rebind_t<std::common_type<int, char>, Types>,
|
||||
Types<int, char>>::value,
|
||||
"");
|
||||
static_assert(std::is_same<rebind_t<std::common_type<>, Types>, Types<>>::value, "");
|
||||
static_assert(std::is_same<rebind_t<std::common_type<int, char>, Types>, Types<int, char>>::value, "");
|
||||
|
||||
// Basic templates.
|
||||
template <typename... Ts> class Future;
|
||||
template <typename... Ts> class Promise;
|
||||
template <typename... Ts> class Boxed;
|
||||
template<typename... Ts>
|
||||
class Future;
|
||||
template<typename... Ts>
|
||||
class Promise;
|
||||
template<typename... Ts>
|
||||
class Boxed;
|
||||
|
||||
template <typename T> struct is_future : std::false_type {};
|
||||
template <typename... Ts> struct is_future<Future<Ts...>> : std::true_type {};
|
||||
template<typename T>
|
||||
struct is_future : std::false_type {};
|
||||
|
||||
template<typename... Ts>
|
||||
struct is_future<Future<Ts...>> : std::true_type {};
|
||||
|
||||
static_assert(!is_future<char *>::value, "");
|
||||
static_assert(is_future<Future<>>::value, "");
|
||||
@ -50,14 +54,19 @@ static_assert(is_future<Future<char *, int>>::value, "");
|
||||
template<typename... Ts>
|
||||
struct is_futures : internal::conjunction<is_future<Ts>...> {};
|
||||
|
||||
template <typename... Ts> struct futurize {
|
||||
template<typename... Ts>
|
||||
struct futurize {
|
||||
using type = Future<Ts...>;
|
||||
};
|
||||
|
||||
template <> struct futurize<void> : futurize<> {};
|
||||
template<>
|
||||
struct futurize<void> : futurize<> {};
|
||||
|
||||
template <typename... Ts> struct futurize<Future<Ts...>> : futurize<Ts...> {};
|
||||
template <typename... Ts> using futurize_t = typename futurize<Ts...>::type;
|
||||
template<typename... Ts>
|
||||
struct futurize<Future<Ts...>> : futurize<Ts...> {};
|
||||
|
||||
template<typename... Ts>
|
||||
using futurize_t = typename futurize<Ts...>::type;
|
||||
|
||||
static_assert(std::is_same<futurize_t<int>, Future<int>>::value, "");
|
||||
static_assert(std::is_same<futurize_t<>, Future<>>::value, "");
|
||||
@ -66,39 +75,41 @@ static_assert(std::is_same<futurize_t<Future<int>>, Future<int>>::value, "");
|
||||
// Concatenate `Ts...` in multiple `Future<Ts...>`
|
||||
// Example:
|
||||
// flatten<Future<T1, T2>, Future<T3>> -> Future<T1, T2, T3>
|
||||
template <typename... Ts> struct flatten {
|
||||
template<typename... Ts>
|
||||
struct flatten {
|
||||
using type = rebind_t<types_cat_t<rebind_t<Ts, Types>...>, Future>;
|
||||
};
|
||||
template <typename... Ts> using flatten_t = typename flatten<Ts...>::type;
|
||||
static_assert(std::is_same<flatten_t<Future<void *>, Future<>, Future<char>>,
|
||||
Future<void *, char>>::value,
|
||||
"");
|
||||
|
||||
template <typename T> using as_boxed_t = rebind_t<T, Boxed>;
|
||||
template <typename T> using as_promise_t = rebind_t<T, Promise>;
|
||||
template<typename... Ts>
|
||||
using flatten_t = typename flatten<Ts...>::type;
|
||||
static_assert(std::is_same<flatten_t<Future<void *>, Future<>, Future<char>>, Future<void *, char>>::value, "");
|
||||
|
||||
template<typename T>
|
||||
using as_boxed_t = rebind_t<T, Boxed>;
|
||||
template<typename T>
|
||||
using as_promise_t = rebind_t<T, Promise>;
|
||||
|
||||
static_assert(std::is_same<as_boxed_t<Future<>>, Boxed<>>::value, "");
|
||||
static_assert(
|
||||
std::is_same<as_boxed_t<Future<int, char>>, Boxed<int, char>>::value, "");
|
||||
static_assert(std::is_same<as_boxed_t<Future<int, char>>, Boxed<int, char>>::value, "");
|
||||
static_assert(std::is_same<as_promise_t<Future<>>, Promise<>>::value, "");
|
||||
static_assert(
|
||||
std::is_same<as_promise_t<Future<int, char>>, Promise<int, char>>::value,
|
||||
"");
|
||||
static_assert(std::is_same<as_promise_t<Future<int, char>>, Promise<int, char>>::value, "");
|
||||
|
||||
template <typename... Ts> struct unboxed_type {
|
||||
using type = typename internal::conditional_t<
|
||||
sizeof...(Ts) == 0, std::common_type<void>,
|
||||
internal::conditional_t<sizeof...(Ts) == 1, std::common_type<Ts...>,
|
||||
typename std::common_type<std::tuple<Ts...>>>>::
|
||||
type;
|
||||
template<typename... Ts>
|
||||
struct unboxed_type {
|
||||
using type =
|
||||
typename internal::conditional_t<sizeof...(Ts) == 0,
|
||||
std::common_type<void>,
|
||||
internal::conditional_t<sizeof...(Ts) == 1,
|
||||
std::common_type<Ts...>,
|
||||
typename std::common_type<std::tuple<Ts...>>>>::type;
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
using unboxed_type_t = typename unboxed_type<Ts...>::type;
|
||||
|
||||
static_assert(std::is_same<unboxed_type_t<>, void>::value, "");
|
||||
static_assert(std::is_same<unboxed_type_t<int>, int>::value, "");
|
||||
static_assert(
|
||||
std::is_same<unboxed_type_t<int, char>, std::tuple<int, char>>::value, "");
|
||||
static_assert(std::is_same<unboxed_type_t<int, char>, std::tuple<int, char>>::value, "");
|
||||
|
||||
}// namespace future
|
||||
}// namespace tile
|
||||
|
@ -13,30 +13,30 @@ namespace tile {
|
||||
namespace future {
|
||||
namespace detail {
|
||||
// T = Boxed<...>
|
||||
template <typename T> T RetrieveBoxed();
|
||||
template<typename T>
|
||||
T RetrieveBoxed();
|
||||
}// namespace detail
|
||||
|
||||
struct box_values_t {
|
||||
explicit box_values_t() = default;
|
||||
};
|
||||
|
||||
constexpr auto box_values = box_values_t();
|
||||
|
||||
template <typename... Ts> class Boxed {
|
||||
template<typename... Ts>
|
||||
class Boxed {
|
||||
|
||||
public:
|
||||
static_assert(!types_contains<Types<Ts...>, void>::value,
|
||||
"Don't allow void in Boxed<Ts...>");
|
||||
static_assert(!types_contains<Types<Ts...>, void>::value, "Don't allow void in Boxed<Ts...>");
|
||||
using value_type = std::tuple<Ts...>;
|
||||
|
||||
using lvalue_ref = internal::add_lvalue_reference_t<unboxed_type_t<Ts...>>;
|
||||
|
||||
using rvalue_ref = internal::add_rvalue_reference_t<unboxed_type_t<Ts...>>;
|
||||
|
||||
template <typename... Us,
|
||||
typename =
|
||||
enable_if_t<std::is_constructible<value_type, Us &&...>::value>>
|
||||
template<typename... Us, typename = enable_if_t<std::is_constructible<value_type, Us &&...>::value>>
|
||||
explicit Boxed(box_values_t, Us &&...imms);
|
||||
template <typename... Us, typename = enable_if_t<std::is_constructible<
|
||||
value_type, std::tuple<Us &&...>>::value>>
|
||||
template<typename... Us, typename = enable_if_t<std::is_constructible<value_type, std::tuple<Us &&...>>::value>>
|
||||
Boxed(Boxed<Us...> boxed);
|
||||
|
||||
lvalue_ref Get() &;
|
||||
@ -48,8 +48,10 @@ public:
|
||||
value_type &&GetRaw() &&;
|
||||
|
||||
private:
|
||||
template <typename T> friend T detail::RetrieveBoxed();
|
||||
template <typename... Us> friend class Boxed;
|
||||
template<typename T>
|
||||
friend T detail::RetrieveBoxed();
|
||||
template<typename... Us>
|
||||
friend class Boxed;
|
||||
|
||||
static constexpr std::size_t kEmpty = 0;
|
||||
static constexpr std::size_t kValue = 1;
|
||||
@ -57,31 +59,32 @@ private:
|
||||
Boxed() = default;
|
||||
|
||||
template<typename... Us>
|
||||
enable_if_t<sizeof...(Us) == 0, lvalue_ref> GetImpl() & {
|
||||
enable_if_t<sizeof...(Us) == 0, lvalue_ref> GetImpl() &
|
||||
{
|
||||
return (void) GetRaw();
|
||||
}
|
||||
|
||||
template<typename... Us>
|
||||
enable_if_t<sizeof...(Us) == 1, lvalue_ref> GetImpl() & {
|
||||
enable_if_t<sizeof...(Us) == 1, lvalue_ref> GetImpl() &
|
||||
{
|
||||
return std::get<0>(GetRaw());
|
||||
}
|
||||
|
||||
template<typename... Us>
|
||||
enable_if_t<(sizeof...(Us) > 1), lvalue_ref> GetImpl() & {
|
||||
enable_if_t<(sizeof...(Us) > 1), lvalue_ref> GetImpl() &
|
||||
{
|
||||
return GetRaw();
|
||||
}
|
||||
|
||||
template<typename... Us>
|
||||
enable_if_t<std::is_void<decltype(std::declval<Boxed<Us...>>().Get())>::value,
|
||||
rvalue_ref>
|
||||
GetMoveImpl() & {
|
||||
enable_if_t<std::is_void<decltype(std::declval<Boxed<Us...>>().Get())>::value, rvalue_ref> GetMoveImpl() &
|
||||
{
|
||||
return Get();
|
||||
}
|
||||
|
||||
template<typename... Us>
|
||||
enable_if_t<
|
||||
!std::is_void<decltype(std::declval<Boxed<Us...>>().Get())>::value,
|
||||
rvalue_ref>
|
||||
GetMoveImpl() & {
|
||||
enable_if_t<!std::is_void<decltype(std::declval<Boxed<Us...>>().Get())>::value, rvalue_ref> GetMoveImpl() &
|
||||
{
|
||||
return std::move(Get());
|
||||
}
|
||||
|
||||
@ -90,50 +93,60 @@ private:
|
||||
};
|
||||
|
||||
static_assert(std::is_void<decltype(std::declval<Boxed<>>().Get())>::value, "");
|
||||
static_assert(!std::is_void<decltype(std::declval<Boxed<int>>().Get())>::value,
|
||||
"");
|
||||
static_assert(
|
||||
!std::is_void<decltype(std::declval<Boxed<int, char>>().Get())>::value, "");
|
||||
static_assert(!std::is_void<decltype(std::declval<Boxed<int>>().Get())>::value, "");
|
||||
static_assert(!std::is_void<decltype(std::declval<Boxed<int, char>>().Get())>::value, "");
|
||||
|
||||
static_assert(
|
||||
std::is_constructible<Boxed<int, double>, box_values_t, int, char>::value,
|
||||
"");
|
||||
static_assert(
|
||||
std::is_constructible<Boxed<int, double>, Boxed<double, float>>::value, "");
|
||||
static_assert(std::is_constructible<Boxed<int, double>, box_values_t, int, char>::value, "");
|
||||
static_assert(std::is_constructible<Boxed<int, double>, Boxed<double, float>>::value, "");
|
||||
|
||||
namespace detail {
|
||||
template <typename T> T RetrieveBoxed() { return T(); }
|
||||
template<typename T>
|
||||
T
|
||||
RetrieveBoxed()
|
||||
{
|
||||
return T();
|
||||
}
|
||||
}// namespace detail
|
||||
|
||||
template<typename... Ts>
|
||||
template<typename... Us, typename>
|
||||
Boxed<Ts...>::Boxed(box_values_t, Us &&...imms)
|
||||
: holding_(std::in_place_index_t<kValue>(), std::forward<Us>(imms)...) {}
|
||||
Boxed<Ts...>::Boxed(box_values_t, Us &&...imms) : holding_(std::in_place_index_t<kValue>(), std::forward<Us>(imms)...)
|
||||
{}
|
||||
|
||||
template<typename... Ts>
|
||||
template<typename... Us, typename>
|
||||
Boxed<Ts...>::Boxed(Boxed<Us...> boxed) {
|
||||
holding_.template emplace<kValue>(
|
||||
mpark::get<kValue>(std::move(boxed).holding_));
|
||||
Boxed<Ts...>::Boxed(Boxed<Us...> boxed)
|
||||
{
|
||||
holding_.template emplace<kValue>(mpark::get<kValue>(std::move(boxed).holding_));
|
||||
}
|
||||
|
||||
template <class... Ts> typename Boxed<Ts...>::lvalue_ref Boxed<Ts...>::Get() & {
|
||||
template<class... Ts>
|
||||
typename Boxed<Ts...>::lvalue_ref
|
||||
Boxed<Ts...>::Get() &
|
||||
{
|
||||
TILE_CHECK_NE(holding_.index(), kEmpty);
|
||||
return GetImpl<Ts...>();
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
typename Boxed<Ts...>::rvalue_ref Boxed<Ts...>::Get() && {
|
||||
typename Boxed<Ts...>::rvalue_ref
|
||||
Boxed<Ts...>::Get() &&
|
||||
{
|
||||
return GetMoveImpl<Ts...>();
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
typename Boxed<Ts...>::value_type &Boxed<Ts...>::GetRaw() & {
|
||||
typename Boxed<Ts...>::value_type &
|
||||
Boxed<Ts...>::GetRaw() &
|
||||
{
|
||||
TILE_CHECK_NE(holding_.index(), kEmpty);
|
||||
return mpark::get<kValue>(holding_);
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
typename Boxed<Ts...>::value_type &&Boxed<Ts...>::GetRaw() && {
|
||||
typename Boxed<Ts...>::value_type &&
|
||||
Boxed<Ts...>::GetRaw() &&
|
||||
{
|
||||
return std::move(GetRaw());
|
||||
}
|
||||
|
||||
@ -145,23 +158,26 @@ namespace future {
|
||||
|
||||
// callable by unboxed
|
||||
template<typename F, typename... Ts>
|
||||
auto CallByBoxed(F &&f, Boxed<Ts...> &&boxed)
|
||||
-> enable_if_t<is_invocable<F, Ts...>::value, invoke_result_t<F, Ts...>> {
|
||||
auto
|
||||
CallByBoxed(F &&f, Boxed<Ts...> &&boxed) -> enable_if_t<is_invocable<F, Ts...>::value, invoke_result_t<F, Ts...>>
|
||||
{
|
||||
return apply(std::forward<F>(f), std::move(boxed.GetRaw()));
|
||||
}
|
||||
|
||||
// callable by boxed
|
||||
template<typename F, typename... Ts>
|
||||
auto CallByBoxed(F &&f, Boxed<Ts...> &&boxed)
|
||||
-> enable_if_t<is_invocable<F, Boxed<Ts...>>::value,
|
||||
invoke_result_t<F, Boxed<Ts...>>> {
|
||||
auto
|
||||
CallByBoxed(F &&f,
|
||||
Boxed<Ts...> &&boxed) -> enable_if_t<is_invocable<F, Boxed<Ts...>>::value, invoke_result_t<F, Boxed<Ts...>>>
|
||||
{
|
||||
return invoke(f, std::move(boxed));
|
||||
}
|
||||
|
||||
template <typename F, typename... Ts> struct call_by_boxed {
|
||||
using type =
|
||||
decltype(CallByBoxed(std::declval<F>(), std::declval<Boxed<Ts...> &&>()));
|
||||
template<typename F, typename... Ts>
|
||||
struct call_by_boxed {
|
||||
using type = decltype(CallByBoxed(std::declval<F>(), std::declval<Boxed<Ts...> &&>()));
|
||||
};
|
||||
|
||||
template<typename F, typename... Ts>
|
||||
using call_by_boxed_t = typename call_by_boxed<F, Ts...>::type;
|
||||
|
||||
|
@ -6,12 +6,12 @@ namespace tile {
|
||||
namespace future {
|
||||
namespace detail {
|
||||
|
||||
TEST(Boxed, Construct) {
|
||||
TEST(Boxed, Construct)
|
||||
{
|
||||
Boxed<> box1(box_values);
|
||||
static_assert(std::is_same<decltype(box1.Get()), void>::value, "");
|
||||
|
||||
static_assert(std::is_void<decltype(std::declval<Boxed<>>().Get())>::value,
|
||||
"");
|
||||
static_assert(std::is_void<decltype(std::declval<Boxed<>>().Get())>::value, "");
|
||||
|
||||
Boxed<int> box2(box_values, 1);
|
||||
EXPECT_EQ(box2.Get(), 1);
|
||||
@ -24,13 +24,15 @@ TEST(Boxed, Construct) {
|
||||
EXPECT_EQ(box4.Get(), std::make_tuple(1, 'a'));
|
||||
}
|
||||
|
||||
TEST(Apply, All) {
|
||||
TEST(Apply, All)
|
||||
{
|
||||
auto f1 = []() { return 1; };
|
||||
auto f2 = [](int x) { return x + 1; };
|
||||
auto f3 = [](int x, int y) { return x + y; };
|
||||
auto t1 = std::make_tuple();
|
||||
auto t2 = std::make_tuple(1);
|
||||
auto t3 = std::make_tuple(1, 2);
|
||||
|
||||
class ApplyTest {
|
||||
public:
|
||||
static void Test() {};
|
||||
@ -48,15 +50,16 @@ TEST(Apply, All) {
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
void Consume(std::function<void(Boxed<Ts...> &&) noexcept> &&action,
|
||||
Boxed<Ts...> &&val) {
|
||||
void
|
||||
Consume(std::function<void(Boxed<Ts...> &&) noexcept> &&action, Boxed<Ts...> &&val)
|
||||
{
|
||||
action(std::move(val));
|
||||
}
|
||||
|
||||
TEST(Boxed, Consume) {
|
||||
TEST(Boxed, Consume)
|
||||
{
|
||||
Boxed<int> box(box_values, 1);
|
||||
std::function<void(Boxed<int> &&) noexcept> l1 = [](Boxed<int> &&) noexcept {
|
||||
};
|
||||
std::function<void(Boxed<int> &&) noexcept> l1 = [](Boxed<int> &&) noexcept {};
|
||||
auto l2 = [](Boxed<int> &&) noexcept {};
|
||||
Consume(std::move(l1), std::move(box));
|
||||
}
|
||||
|
@ -12,7 +12,8 @@
|
||||
|
||||
namespace tile {
|
||||
namespace future {
|
||||
template <typename... Ts> class Core {
|
||||
template<typename... Ts>
|
||||
class Core {
|
||||
public:
|
||||
using value_type = Boxed<Ts...>;
|
||||
using action_type = std::function<void(value_type &&) noexcept>;
|
||||
@ -26,14 +27,14 @@ public:
|
||||
private:
|
||||
struct WaitingForSingleObject {
|
||||
WaitingForSingleObject() {}
|
||||
WaitingForSingleObject(action_type &&action)
|
||||
: on_satisfied(std::move(action)) {}
|
||||
|
||||
WaitingForSingleObject(action_type &&action) : on_satisfied(std::move(action)) {}
|
||||
|
||||
action_type on_satisfied;
|
||||
};
|
||||
|
||||
struct Satisfied {
|
||||
Satisfied(value_type &&v, bool e)
|
||||
: value(std::move(v)), ever_called_continuation(e) {}
|
||||
Satisfied(value_type &&v, bool e) : value(std::move(v)), ever_called_continuation(e) {}
|
||||
|
||||
value_type value;
|
||||
bool ever_called_continuation{false};
|
||||
@ -47,10 +48,11 @@ private:
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
void Core<Ts...>::SetBoxed(value_type &&value) noexcept {
|
||||
void
|
||||
Core<Ts...>::SetBoxed(value_type &&value) noexcept
|
||||
{
|
||||
UniqueLock<Mutex> lk(lock_);
|
||||
action_type action =
|
||||
std::move(mpark::get<WaitingForSingleObject>(state_).on_satisfied);
|
||||
action_type action = std::move(mpark::get<WaitingForSingleObject>(state_).on_satisfied);
|
||||
|
||||
state_.template emplace<Satisfied>(Satisfied{std::move(value), !!action});
|
||||
|
||||
@ -58,39 +60,36 @@ void Core<Ts...>::SetBoxed(value_type &&value) noexcept {
|
||||
lk.Unlock();
|
||||
|
||||
auto moved_action = MakeMoveOnCopy(std::move(action));
|
||||
auto moved_value =
|
||||
MakeMoveOnCopy(std::move(mpark::get<Satisfied>(state_).value));
|
||||
executor_.Execute([moved_action, moved_value]() mutable noexcept {
|
||||
moved_action.Ref()(moved_value.Move());
|
||||
});
|
||||
auto moved_value = MakeMoveOnCopy(std::move(mpark::get<Satisfied>(state_).value));
|
||||
executor_.Execute([moved_action, moved_value]() mutable noexcept { moved_action.Ref()(moved_value.Move()); });
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
void Core<Ts...>::ChainAction(action_type action) noexcept {
|
||||
void
|
||||
Core<Ts...>::ChainAction(action_type action) noexcept
|
||||
{
|
||||
UniqueLock<Mutex> lk(lock_);
|
||||
if (state_.index() == 0) {
|
||||
// waiting for value
|
||||
state_.template emplace<WaitingForSingleObject>(
|
||||
WaitingForSingleObject{std::move(action)});
|
||||
state_.template emplace<WaitingForSingleObject>(WaitingForSingleObject{std::move(action)});
|
||||
} else {
|
||||
TILE_CHECK_EQ(state_.index(), 1);
|
||||
auto &&s = mpark::get<Satisfied>(state_);
|
||||
TILE_CHECK(!s.ever_called_continuation,
|
||||
"Action may not be chained for multiple times.");
|
||||
TILE_CHECK(!s.ever_called_continuation, "Action may not be chained for multiple times.");
|
||||
|
||||
s.ever_called_continuation = true;
|
||||
lk.Unlock();
|
||||
auto moved_action = MakeMoveOnCopy(std::move(action));
|
||||
auto moved_value = MakeMoveOnCopy(std::move(s.value));
|
||||
executor_.Execute([moved_action, moved_value]() mutable noexcept {
|
||||
moved_action.Ref()(moved_value.Move());
|
||||
});
|
||||
executor_.Execute([moved_action, moved_value]() mutable noexcept { moved_action.Ref()(moved_value.Move()); });
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
inline Executor Core<Ts...>::GetExecutor() const noexcept {
|
||||
inline Executor
|
||||
Core<Ts...>::GetExecutor() const noexcept
|
||||
{
|
||||
return executor_;
|
||||
}
|
||||
}// namespace future
|
||||
|
@ -19,23 +19,28 @@ public:
|
||||
|
||||
// Copy & move
|
||||
Executor(const Executor &other) : impl_(other.impl_->Clone()) {}
|
||||
Executor &operator=(const Executor &e) {
|
||||
|
||||
Executor &operator=(const Executor &e)
|
||||
{
|
||||
impl_ = e.impl_->Clone();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Executor(Executor &&other) = default;
|
||||
Executor &operator=(Executor &&) = default;
|
||||
|
||||
// Base Executor, Has Execute function
|
||||
template<typename T,
|
||||
typename = enable_if_t<!std::is_same<Executor, decay_t<T>>::value>,
|
||||
typename = decltype(std::declval<T &&>().Execute(
|
||||
std::declval<std::function<void()>>()))>
|
||||
Executor(T &&executor) {
|
||||
typename = decltype(std::declval<T &&>().Execute(std::declval<std::function<void()>>()))>
|
||||
Executor(T &&executor)
|
||||
{
|
||||
impl_ = EraseExecutorType(std::forward<T>(executor));
|
||||
}
|
||||
|
||||
template <typename T> void Execute(T &&job) {
|
||||
template<typename T>
|
||||
void Execute(T &&job)
|
||||
{
|
||||
job();
|
||||
// impl_->Execute(std::forward<T>(job));
|
||||
}
|
||||
@ -48,28 +53,25 @@ private:
|
||||
virtual std::unique_ptr<ConcreteExecutor> Clone() = 0;
|
||||
};
|
||||
|
||||
template <typename T> class ConcreteExecutorImpl : public ConcreteExecutor {
|
||||
template<typename T>
|
||||
class ConcreteExecutorImpl : public ConcreteExecutor {
|
||||
public:
|
||||
template <typename U, typename = decltype(std::declval<U>().Execute(
|
||||
std::declval<std::function<void()>>()))>
|
||||
explicit ConcreteExecutorImpl(U &&e) : impl_(std::forward<U>(e)) {}
|
||||
template<typename U, typename = decltype(std::declval<U>().Execute(std::declval<std::function<void()>>()))>
|
||||
explicit ConcreteExecutorImpl(U &&e) : impl_(std::forward<U>(e))
|
||||
{}
|
||||
|
||||
void Execute(std::function<void()> job) override {
|
||||
impl_.Execute(std::move(job));
|
||||
}
|
||||
void Execute(std::function<void()> job) override { impl_.Execute(std::move(job)); }
|
||||
|
||||
std::unique_ptr<ConcreteExecutor> Clone() override {
|
||||
return make_unique<ConcreteExecutorImpl>(impl_);
|
||||
}
|
||||
std::unique_ptr<ConcreteExecutor> Clone() override { return make_unique<ConcreteExecutorImpl>(impl_); }
|
||||
|
||||
private:
|
||||
T impl_;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
std::unique_ptr<ConcreteExecutor> EraseExecutorType(T &&executor) {
|
||||
return make_unique<ConcreteExecutorImpl<typename std::decay<T>::type>>(
|
||||
std::forward<T>(executor));
|
||||
std::unique_ptr<ConcreteExecutor> EraseExecutorType(T &&executor)
|
||||
{
|
||||
return make_unique<ConcreteExecutorImpl<typename std::decay<T>::type>>(std::forward<T>(executor));
|
||||
}
|
||||
|
||||
std::unique_ptr<ConcreteExecutor> impl_;
|
||||
@ -81,22 +83,25 @@ public:
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
inline std::shared_ptr<Executor> &DefaultExecutorPtr() {
|
||||
static std::shared_ptr<Executor> default_executor =
|
||||
std::make_shared<Executor>(InlineExecutor());
|
||||
inline std::shared_ptr<Executor> &
|
||||
DefaultExecutorPtr()
|
||||
{
|
||||
static std::shared_ptr<Executor> default_executor = std::make_shared<Executor>(InlineExecutor());
|
||||
return default_executor;
|
||||
}
|
||||
}// namespace detail
|
||||
|
||||
inline Executor GetDefaultExecutor() {
|
||||
return *std::atomic_load_explicit(&detail::DefaultExecutorPtr(),
|
||||
std::memory_order_acquire);
|
||||
inline Executor
|
||||
GetDefaultExecutor()
|
||||
{
|
||||
return *std::atomic_load_explicit(&detail::DefaultExecutorPtr(), std::memory_order_acquire);
|
||||
}
|
||||
|
||||
inline Executor SetDefaultExecutor(Executor exec) {
|
||||
inline Executor
|
||||
SetDefaultExecutor(Executor exec)
|
||||
{
|
||||
auto ptr = std::make_shared<Executor>(std::move(exec));
|
||||
return *std::atomic_exchange_explicit(&detail::DefaultExecutorPtr(), ptr,
|
||||
std::memory_order_release);
|
||||
return *std::atomic_exchange_explicit(&detail::DefaultExecutorPtr(), ptr, std::memory_order_release);
|
||||
}
|
||||
|
||||
}// namespace future
|
||||
|
@ -10,29 +10,31 @@ namespace tile {
|
||||
namespace future {
|
||||
template<typename... Ts>
|
||||
template<typename... Us, typename>
|
||||
Future<Ts...>::Future(futurize_values_t, Us &&...imms)
|
||||
: core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor())) {
|
||||
Future<Ts...>::Future(futurize_values_t, Us &&...imms) : core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor()))
|
||||
{
|
||||
core_->SetBoxed(Boxed<Ts...>(box_values, std::forward<Us>(imms)...));
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
template<typename... Us, typename>
|
||||
Future<Ts...>::Future(futurize_tuple_t, std::tuple<Us...> values)
|
||||
: core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor())) {
|
||||
: core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor()))
|
||||
{
|
||||
core_->SetBoxed(Boxed<Ts...>(box_values, std::move(values)));
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
template<class U, class>
|
||||
Future<Ts...>::Future(U &&value)
|
||||
: core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor())) {
|
||||
Future<Ts...>::Future(U &&value) : core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor()))
|
||||
{
|
||||
static_assert(sizeof...(Ts) == 1, "");
|
||||
core_->SetBoxed(Boxed<Ts...>(box_values, std::forward<U>(value)));
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
template<class... Us, class>
|
||||
Future<Ts...>::Future(Future<Us...> &&future) {
|
||||
Future<Ts...>::Future(Future<Us...> &&future)
|
||||
{
|
||||
Promise<Ts...> p;
|
||||
|
||||
// Here we "steal" `p.GetFuture()`'s core and "install" it into ourself,
|
||||
@ -43,10 +45,12 @@ Future<Ts...>::Future(Future<Us...> &&future) {
|
||||
moved_promise->SetBoxed(std::move(boxed));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
template<typename F, typename R>
|
||||
auto Future<Ts...>::Then(F &&fun)
|
||||
-> enable_if_t<std::is_void<R>::value, Future<>> {
|
||||
auto
|
||||
Future<Ts...>::Then(F &&fun) -> enable_if_t<std::is_void<R>::value, Future<>>
|
||||
{
|
||||
using NewFuture = futurize_t<R>;
|
||||
as_promise_t<NewFuture> promise(core_->GetExecutor());
|
||||
|
||||
@ -70,9 +74,9 @@ auto Future<Ts...>::Then(F &&fun)
|
||||
// if R is `Future<...>`
|
||||
template<typename... Ts>
|
||||
template<typename F, typename R>
|
||||
auto Future<Ts...>::Then(F &&fun)
|
||||
-> enable_if_t<!std::is_void<R>::value && is_future<R>::value,
|
||||
futurize_t<R>> {
|
||||
auto
|
||||
Future<Ts...>::Then(F &&fun) -> enable_if_t<!std::is_void<R>::value && is_future<R>::value, futurize_t<R>>
|
||||
{
|
||||
using NewFuture = futurize_t<R>;
|
||||
as_promise_t<NewFuture> promise(core_->GetExecutor());
|
||||
|
||||
@ -81,18 +85,13 @@ auto Future<Ts...>::Then(F &&fun)
|
||||
auto moved_promise = MakeMoveOnCopy(promise);
|
||||
auto moved_fun = MakeMoveOnCopy(std::forward<F>(fun));
|
||||
|
||||
auto cont = [moved_promise,
|
||||
moved_fun](Boxed<Ts...> &&value) mutable noexcept {
|
||||
auto cont = [moved_promise, moved_fun](Boxed<Ts...> &&value) mutable noexcept {
|
||||
auto future = CallByBoxed(moved_fun.Move(), std::move(value));
|
||||
// R is Future<Us...>
|
||||
// build a lambda to set the value of the promise
|
||||
|
||||
future.core_->ChainAction(
|
||||
[moved_promise](as_boxed_t<NewFuture> &&nested_v) mutable noexcept {
|
||||
static_assert(
|
||||
std::is_same<as_boxed_t<NewFuture>,
|
||||
remove_reference_t<decltype(nested_v)>>::value,
|
||||
"");
|
||||
future.core_->ChainAction([moved_promise](as_boxed_t<NewFuture> &&nested_v) mutable noexcept {
|
||||
static_assert(std::is_same<as_boxed_t<NewFuture>, remove_reference_t<decltype(nested_v)>>::value, "");
|
||||
moved_promise->SetBoxed(std::move(nested_v));
|
||||
});
|
||||
};
|
||||
@ -104,9 +103,9 @@ auto Future<Ts...>::Then(F &&fun)
|
||||
|
||||
template<typename... Ts>
|
||||
template<typename F, typename R>
|
||||
auto Future<Ts...>::Then(F &&fun)
|
||||
-> enable_if_t<!std::is_void<R>::value && !is_future<R>::value,
|
||||
futurize_t<R>> {
|
||||
auto
|
||||
Future<Ts...>::Then(F &&fun) -> enable_if_t<!std::is_void<R>::value && !is_future<R>::value, futurize_t<R>>
|
||||
{
|
||||
using NewFuture = futurize_t<R>;
|
||||
as_promise_t<NewFuture> promise(core_->GetExecutor());
|
||||
|
||||
@ -116,8 +115,7 @@ auto Future<Ts...>::Then(F &&fun)
|
||||
auto moved_fun = MakeMoveOnCopy(std::forward<F>(fun));
|
||||
|
||||
auto cont = [moved_promise, moved_fun](Boxed<Ts...> &&value) mutable {
|
||||
as_boxed_t<NewFuture> boxed_value(
|
||||
box_values, CallByBoxed(moved_fun.Move(), std::move(value)));
|
||||
as_boxed_t<NewFuture> boxed_value(box_values, CallByBoxed(moved_fun.Move(), std::move(value)));
|
||||
moved_promise->SetBoxed(std::move(boxed_value));
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@ namespace future {
|
||||
struct futurize_values_t {
|
||||
explicit futurize_values_t() = default;
|
||||
};
|
||||
|
||||
struct futurize_tuple_t {
|
||||
explicit futurize_tuple_t() = default;
|
||||
};
|
||||
@ -19,29 +20,26 @@ struct futurize_tuple_t {
|
||||
constexpr auto futurize_values = futurize_values_t();
|
||||
constexpr auto futurize_tuple = futurize_tuple_t();
|
||||
|
||||
template <typename... Ts> class Future {
|
||||
template<typename... Ts>
|
||||
class Future {
|
||||
public:
|
||||
using action_type = typename Core<Ts...>::action_type;
|
||||
template <typename... Us, typename = enable_if_t<std::is_constructible<
|
||||
Boxed<Ts...>, box_values_t, Us &&...>::value>>
|
||||
template<typename... Us, typename = enable_if_t<std::is_constructible<Boxed<Ts...>, box_values_t, Us &&...>::value>>
|
||||
explicit Future(futurize_values_t, Us &&...imms);
|
||||
|
||||
template <typename... Us, typename = enable_if_t<std::is_constructible<
|
||||
Boxed<Ts...>, Boxed<Us...>>::value>>
|
||||
template<typename... Us, typename = enable_if_t<std::is_constructible<Boxed<Ts...>, Boxed<Us...>>::value>>
|
||||
Future(futurize_tuple_t, std::tuple<Us...> values);
|
||||
|
||||
template<
|
||||
typename U,
|
||||
typename = enable_if_t<
|
||||
std::is_constructible<typename internal::conditional_t<
|
||||
sizeof...(Ts) == 1, std::common_type<Ts...>,
|
||||
typename = enable_if_t<std::is_constructible<typename internal::conditional_t<sizeof...(Ts) == 1,
|
||||
std::common_type<Ts...>,
|
||||
std::common_type<void>>::type,
|
||||
U>::value &&
|
||||
!is_future<U>::value>>
|
||||
U>::value
|
||||
&& !is_future<U>::value>>
|
||||
Future(U &&value);
|
||||
|
||||
template <typename... Us, typename = enable_if_t<std::is_constructible<
|
||||
Boxed<Ts...>, Boxed<Us...>>::value>>
|
||||
template<typename... Us, typename = enable_if_t<std::is_constructible<Boxed<Ts...>, Boxed<Us...>>::value>>
|
||||
Future(Future<Us...> &&future);
|
||||
|
||||
Future() = default;
|
||||
@ -52,8 +50,7 @@ public:
|
||||
Future(Future &&) = default;
|
||||
Future &operator=(Future &&) = default;
|
||||
|
||||
static_assert(
|
||||
!types_contains<Types<Ts...>, void>::value,
|
||||
static_assert(!types_contains<Types<Ts...>, void>::value,
|
||||
"There's no point in specifying `void` `Ts...`, "
|
||||
"use `Future<>` if you meant to declare a future with no value.");
|
||||
|
||||
@ -61,20 +58,18 @@ public:
|
||||
auto Then(F &&fun) -> enable_if_t<std::is_void<R>::value, Future<>>;
|
||||
|
||||
template<typename F, typename R = call_by_boxed_t<F, Ts...>>
|
||||
auto
|
||||
Then(F &&fun) -> enable_if_t<!std::is_void<R>::value && is_future<R>::value,
|
||||
futurize_t<R>>;
|
||||
auto Then(F &&fun) -> enable_if_t<!std::is_void<R>::value && is_future<R>::value, futurize_t<R>>;
|
||||
|
||||
template<typename F, typename R = call_by_boxed_t<F, Ts...>>
|
||||
auto
|
||||
Then(F &&fun) -> enable_if_t<!std::is_void<R>::value && !is_future<R>::value,
|
||||
futurize_t<R>>;
|
||||
auto Then(F &&fun) -> enable_if_t<!std::is_void<R>::value && !is_future<R>::value, futurize_t<R>>;
|
||||
|
||||
private:
|
||||
friend class Promise<Ts...>;
|
||||
template <typename... Us> friend class Future;
|
||||
template<typename... Us>
|
||||
friend class Future;
|
||||
|
||||
explicit Future(std::shared_ptr<Core<Ts...>> core) : core_(std::move(core)) {}
|
||||
|
||||
std::shared_ptr<Core<Ts...>> core_;
|
||||
};
|
||||
|
||||
|
@ -27,14 +27,13 @@ static_assert(std::is_move_assignable<Promise<>>::value, "");
|
||||
static_assert(std::is_move_constructible<Promise<int, double>>::value, "");
|
||||
static_assert(std::is_move_assignable<Promise<int, double>>::value, "");
|
||||
|
||||
static_assert(!std::is_copy_constructible<future::Boxed<MoveOnlyType>>::value,
|
||||
"");
|
||||
static_assert(!std::is_copy_constructible<future::Boxed<MoveOnlyType>>::value, "");
|
||||
static_assert(!std::is_copy_assignable<future::Boxed<MoveOnlyType>>::value, "");
|
||||
static_assert(std::is_move_constructible<future::Boxed<MoveOnlyType>>::value,
|
||||
"");
|
||||
static_assert(std::is_move_constructible<future::Boxed<MoveOnlyType>>::value, "");
|
||||
static_assert(std::is_move_assignable<future::Boxed<MoveOnlyType>>::value, "");
|
||||
|
||||
template <typename T> using resource_ptr = std::unique_ptr<T, void (*)(T *)>;
|
||||
template<typename T>
|
||||
using resource_ptr = std::unique_ptr<T, void (*)(T *)>;
|
||||
|
||||
struct NoCopy {
|
||||
NoCopy() = default;
|
||||
@ -44,7 +43,9 @@ struct NoCopy {
|
||||
NoCopy &operator=(NoCopy &&) = default;
|
||||
};
|
||||
|
||||
Future<resource_ptr<void>, int> AcquireXxxAsync() {
|
||||
Future<resource_ptr<void>, int>
|
||||
AcquireXxxAsync()
|
||||
{
|
||||
Promise<resource_ptr<void>, int> p;
|
||||
auto rc = p.GetFuture();
|
||||
|
||||
@ -62,7 +63,8 @@ Future<resource_ptr<void>, int> AcquireXxxAsync() {
|
||||
return rc;
|
||||
}
|
||||
|
||||
TEST(Future, Initialization) {
|
||||
TEST(Future, Initialization)
|
||||
{
|
||||
// Uninitialized future
|
||||
Future<> uf1;
|
||||
Future<int, double> uf2;
|
||||
@ -82,22 +84,23 @@ TEST(Future, Initialization) {
|
||||
ASSERT_EQ(2.0, std::get<1>(BlockingGet(std::move(mf))));
|
||||
}
|
||||
|
||||
TEST(Future, WhenAll) {
|
||||
TEST(Future, WhenAll)
|
||||
{
|
||||
static FILE *const kDummyFile = reinterpret_cast<FILE *>(0x10000123);
|
||||
|
||||
Future<FILE *, int> async_file(futurize_values, kDummyFile, 0);
|
||||
Future<FILE *, int> failure_file(futurize_values, nullptr, -1);
|
||||
Future<resource_ptr<FILE>, int> move_only_file{
|
||||
futurize_values, resource_ptr<FILE>{nullptr, nullptr}, -2};
|
||||
futurize_values, resource_ptr<FILE>{nullptr, nullptr},
|
||||
-2
|
||||
};
|
||||
|
||||
Future<> void_op(futurize_values);
|
||||
|
||||
bool called = false;
|
||||
|
||||
WhenAll(std::move(async_file), std::move(failure_file), std::move(void_op),
|
||||
std::move(move_only_file))
|
||||
.Then([&](std::tuple<FILE *, int> af, std::tuple<FILE *, int> ff,
|
||||
std::tuple<resource_ptr<FILE>, int> mof) {
|
||||
WhenAll(std::move(async_file), std::move(failure_file), std::move(void_op), std::move(move_only_file))
|
||||
.Then([&](std::tuple<FILE *, int> af, std::tuple<FILE *, int> ff, std::tuple<resource_ptr<FILE>, int> mof) {
|
||||
ASSERT_EQ(kDummyFile, std::get<0>(af));
|
||||
ASSERT_EQ(0, std::get<1>(af));
|
||||
ASSERT_EQ(nullptr, std::get<0>(ff));
|
||||
@ -111,11 +114,11 @@ TEST(Future, WhenAll) {
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
|
||||
TEST(Future, WhenAllForOtherThread) {
|
||||
TEST(Future, WhenAllForOtherThread)
|
||||
{
|
||||
bool called = false;
|
||||
BlockingGet(WhenAll(AcquireXxxAsync(), AcquireXxxAsync())
|
||||
.Then([&](std::tuple<resource_ptr<void>, int> a,
|
||||
std::tuple<resource_ptr<void>, int> b) {
|
||||
.Then([&](std::tuple<resource_ptr<void>, int> a, std::tuple<resource_ptr<void>, int> b) {
|
||||
ASSERT_NE(nullptr, std::get<0>(a));
|
||||
ASSERT_EQ(0, std::get<1>(a));
|
||||
ASSERT_NE(nullptr, std::get<0>(b));
|
||||
@ -125,17 +128,16 @@ TEST(Future, WhenAllForOtherThread) {
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
|
||||
TEST(Future, WhenAllMultiThreaded) {
|
||||
TEST(Future, WhenAllMultiThreaded)
|
||||
{
|
||||
for (int i = 0; i != 100; ++i) {
|
||||
bool called = false;
|
||||
Promise<std::unique_ptr<int>, std::unique_ptr<char>> p1;
|
||||
Promise<> p2;
|
||||
Latch latch(2 + 1);
|
||||
|
||||
auto all =
|
||||
WhenAll(p1.GetFuture(), p2.GetFuture())
|
||||
.Then([&](std::tuple<std::unique_ptr<int>, std::unique_ptr<char>>
|
||||
&&p) {
|
||||
auto all = WhenAll(p1.GetFuture(), p2.GetFuture())
|
||||
.Then([&](std::tuple<std::unique_ptr<int>, std::unique_ptr<char>> &&p) {
|
||||
ASSERT_NE(nullptr, std::get<0>(p));
|
||||
ASSERT_EQ(nullptr, std::get<1>(p));
|
||||
|
||||
@ -163,7 +165,8 @@ TEST(Future, WhenAllMultiThreaded) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Future, WhenAllcollectionMultiThreaded) {
|
||||
TEST(Future, WhenAllcollectionMultiThreaded)
|
||||
{
|
||||
for (int i = 0; i != 100; ++i) {
|
||||
constexpr auto kCount = 100;
|
||||
|
||||
@ -173,9 +176,7 @@ TEST(Future, WhenAllcollectionMultiThreaded) {
|
||||
|
||||
Latch latch(kCount + 1);
|
||||
std::atomic<bool> called{false};
|
||||
for (auto &p : vps) {
|
||||
vfs.emplace_back(p.GetFuture());
|
||||
}
|
||||
for (auto &p : vps) { vfs.emplace_back(p.GetFuture()); }
|
||||
|
||||
WhenAll(&vfs).Then([&](std::vector<bool> v) {
|
||||
ASSERT_TRUE(std::all_of(v.begin(), v.end(), [&](bool v) { return v; }));
|
||||
@ -190,14 +191,13 @@ TEST(Future, WhenAllcollectionMultiThreaded) {
|
||||
}
|
||||
ASSERT_FALSE(called);
|
||||
latch.CountDown();
|
||||
for (auto &&e : ts) {
|
||||
e.join();
|
||||
}
|
||||
for (auto &&e : ts) { e.join(); }
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Future, WhenAllCollectionEmpty) {
|
||||
TEST(Future, WhenAllCollectionEmpty)
|
||||
{
|
||||
{
|
||||
std::vector<Future<>> vfs;
|
||||
int x{};
|
||||
@ -213,13 +213,12 @@ TEST(Future, WhenAllCollectionEmpty) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Future, WhenAllForCollectionOfEmptyFuture) {
|
||||
TEST(Future, WhenAllForCollectionOfEmptyFuture)
|
||||
{
|
||||
std::vector<Future<>> vfs;
|
||||
bool cont_called = false;
|
||||
|
||||
for (int i = 0; i != 1000; ++i) {
|
||||
vfs.emplace_back(futurize_values);
|
||||
}
|
||||
for (int i = 0; i != 1000; ++i) { vfs.emplace_back(futurize_values); }
|
||||
|
||||
// std::vector<Future<>> is special, the continuation is called with
|
||||
// no argument.
|
||||
@ -227,18 +226,18 @@ TEST(Future, WhenAllForCollectionOfEmptyFuture) {
|
||||
ASSERT_TRUE(cont_called);
|
||||
}
|
||||
|
||||
TEST(Future, WhenAnyCollectionOfEmptyFuture) {
|
||||
TEST(Future, WhenAnyCollectionOfEmptyFuture)
|
||||
{
|
||||
std::vector<Future<>> vfs;
|
||||
bool called = false;
|
||||
for (int i = 0; i != 1000; ++i) {
|
||||
vfs.emplace_back(futurize_values);
|
||||
}
|
||||
for (int i = 0; i != 1000; ++i) { vfs.emplace_back(futurize_values); }
|
||||
|
||||
WhenAny(std::move(vfs)).Then([&](std::size_t index) { called = true; });
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
|
||||
TEST(Future, WhenAnyCollectionMultiThreaded) {
|
||||
TEST(Future, WhenAnyCollectionMultiThreaded)
|
||||
{
|
||||
for (int i = 0; i != 100; ++i) {
|
||||
constexpr auto kCount = 100;
|
||||
std::vector<Promise<std::unique_ptr<int>, char>> vps(kCount);
|
||||
@ -248,8 +247,7 @@ TEST(Future, WhenAnyCollectionMultiThreaded) {
|
||||
std::atomic<int> x{};
|
||||
|
||||
for (auto &&e : vps) {
|
||||
vfs.emplace_back(
|
||||
e.GetFuture().Then([&](std::unique_ptr<int> &&setter_index, char &&) {
|
||||
vfs.emplace_back(e.GetFuture().Then([&](std::unique_ptr<int> &&setter_index, char &&) {
|
||||
++x;
|
||||
return *setter_index;
|
||||
}));
|
||||
@ -280,15 +278,14 @@ TEST(Future, WhenAnyCollectionMultiThreaded) {
|
||||
ASSERT_LE(0, index);
|
||||
ASSERT_EQ(index, value);
|
||||
|
||||
for (auto &&e : ts) {
|
||||
e.join();
|
||||
}
|
||||
for (auto &&e : ts) { e.join(); }
|
||||
starter.join();
|
||||
ASSERT_EQ(kCount, x);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Future, DurationTimeout) {
|
||||
TEST(Future, DurationTimeout)
|
||||
{
|
||||
auto one_sec = std::chrono::seconds(1);
|
||||
{
|
||||
Promise<int> p;
|
||||
|
@ -11,15 +11,22 @@
|
||||
namespace tile {
|
||||
namespace future {
|
||||
namespace detail {
|
||||
template <std::size_t I, typename F> void for_each_indexed_impl(F &&) {}
|
||||
template<std::size_t I, typename F>
|
||||
void
|
||||
for_each_indexed_impl(F &&)
|
||||
{}
|
||||
|
||||
template<std::size_t I, typename F, typename T>
|
||||
void for_each_indexed_impl(F &&f, T &&t) {
|
||||
void
|
||||
for_each_indexed_impl(F &&f, T &&t)
|
||||
{
|
||||
f(std::forward<T>(t), std::integral_constant<std::size_t, I>());
|
||||
}
|
||||
|
||||
template<std::size_t I, typename F, typename T, typename... Ts>
|
||||
void for_each_indexed_impl(F &&f, T &&t, Ts &&...ts) {
|
||||
void
|
||||
for_each_indexed_impl(F &&f, T &&t, Ts &&...ts)
|
||||
{
|
||||
f(std::forward<T>(t), std::integral_constant<std::size_t, I>());
|
||||
|
||||
for_each_indexed_impl<I + 1>(std::forward<F>(f), std::forward<Ts>(ts)...);
|
||||
@ -31,12 +38,14 @@ struct is_template_t : std::false_type {};
|
||||
template<template<typename...> class TT, typename... T>
|
||||
struct is_template_t<TT, TT<T...>> : std::true_type {};
|
||||
|
||||
template <typename T> struct is_duration {
|
||||
template<typename T>
|
||||
struct is_duration {
|
||||
static constexpr bool value = is_template_t<std::chrono::duration, T>::value;
|
||||
};
|
||||
template <typename T> struct is_time_point {
|
||||
static constexpr bool value =
|
||||
is_template_t<std::chrono::time_point, T>::value;
|
||||
|
||||
template<typename T>
|
||||
struct is_time_point {
|
||||
static constexpr bool value = is_template_t<std::chrono::time_point, T>::value;
|
||||
};
|
||||
|
||||
static_assert(is_duration<std::chrono::steady_clock::duration>::value, "");
|
||||
@ -44,10 +53,13 @@ static_assert(!is_duration<std::chrono::steady_clock::time_point>::value, "");
|
||||
static_assert(!is_time_point<std::chrono::steady_clock::duration>::value, "");
|
||||
static_assert(is_time_point<std::chrono::steady_clock::time_point>::value, "");
|
||||
|
||||
template <typename T> struct optional_or_bool {
|
||||
template<typename T>
|
||||
struct optional_or_bool {
|
||||
using type = std::optional<T>;
|
||||
};
|
||||
template <> struct optional_or_bool<void> {
|
||||
|
||||
template<>
|
||||
struct optional_or_bool<void> {
|
||||
using type = bool;
|
||||
};
|
||||
|
||||
@ -56,7 +68,10 @@ using optional_or_bool_t = typename optional_or_bool<T>::type;
|
||||
|
||||
}// namespace detail
|
||||
|
||||
template <typename F, typename... Ts> void for_each_indexed(F &&f, Ts &&...ts) {
|
||||
template<typename F, typename... Ts>
|
||||
void
|
||||
for_each_indexed(F &&f, Ts &&...ts)
|
||||
{
|
||||
detail::for_each_indexed_impl<0>(std::forward<F>(f), std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
|
@ -14,26 +14,34 @@
|
||||
namespace tile {
|
||||
namespace future {
|
||||
template<class... Ts>
|
||||
Promise<Ts...>::Promise()
|
||||
: core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor())) {}
|
||||
Promise<Ts...>::Promise() : core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor()))
|
||||
{}
|
||||
|
||||
template <class... Ts> Future<Ts...> Promise<Ts...>::GetFuture() {
|
||||
template<class... Ts>
|
||||
Future<Ts...>
|
||||
Promise<Ts...>::GetFuture()
|
||||
{
|
||||
return Future<Ts...>(core_);
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
template<class... Us, class>
|
||||
void Promise<Ts...>::SetValue(Us &&...values) {
|
||||
void
|
||||
Promise<Ts...>::SetValue(Us &&...values)
|
||||
{
|
||||
core_->SetBoxed(Boxed<Ts...>(box_values, std::forward<Us>(values)...));
|
||||
}
|
||||
|
||||
template <class... Ts> void Promise<Ts...>::SetBoxed(Boxed<Ts...> boxed) {
|
||||
template<class... Ts>
|
||||
void
|
||||
Promise<Ts...>::SetBoxed(Boxed<Ts...> boxed)
|
||||
{
|
||||
core_->SetBoxed(std::move(boxed));
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
Promise<Ts...>::Promise(Executor executor)
|
||||
: core_(std::make_shared<Core<Ts...>>(std::move(executor))) {}
|
||||
Promise<Ts...>::Promise(Executor executor) : core_(std::make_shared<Core<Ts...>>(std::move(executor)))
|
||||
{}
|
||||
|
||||
}// namespace future
|
||||
}// namespace tile
|
||||
|
@ -9,7 +9,8 @@
|
||||
namespace tile {
|
||||
namespace future {
|
||||
|
||||
template <typename... Ts> class Promise {
|
||||
template<typename... Ts>
|
||||
class Promise {
|
||||
public:
|
||||
using value_type = std::tuple<Ts...>;
|
||||
|
||||
@ -27,15 +28,14 @@ public:
|
||||
"`Promise<>` if you meant to declare a future with no value.");
|
||||
|
||||
Future<Ts...> GetFuture();
|
||||
template <typename... Us,
|
||||
typename =
|
||||
enable_if_t<std::is_constructible<value_type, Us &&...>::value>>
|
||||
template<typename... Us, typename = enable_if_t<std::is_constructible<value_type, Us &&...>::value>>
|
||||
void SetValue(Us &&...values);
|
||||
|
||||
void SetBoxed(Boxed<Ts...> boxed);
|
||||
|
||||
private:
|
||||
template <typename... Us> friend class Future;
|
||||
template<typename... Us>
|
||||
friend class Future;
|
||||
|
||||
explicit Promise(Executor executor);
|
||||
std::shared_ptr<Core<Ts...>> core_;
|
||||
|
@ -14,14 +14,14 @@ namespace future {
|
||||
namespace detail {
|
||||
|
||||
template<typename... Ts>
|
||||
auto SplitImpl(Future<Ts...> *future)
|
||||
-> enable_if_t<(sizeof...(Ts) > 0), Future<Ts...>> {
|
||||
auto
|
||||
SplitImpl(Future<Ts...> *future) -> enable_if_t<(sizeof...(Ts) > 0), Future<Ts...>>
|
||||
{
|
||||
Promise<Ts...> promise;
|
||||
auto rc = promise.GetFuture();
|
||||
|
||||
auto moved_promise = MakeMoveOnCopy(promise);
|
||||
*future =
|
||||
std::move(*future).Then([moved_promise](Boxed<Ts...> boxed) mutable {
|
||||
*future = std::move(*future).Then([moved_promise](Boxed<Ts...> boxed) mutable {
|
||||
moved_promise->SetBoxed(boxed);
|
||||
|
||||
return std::move(boxed.Get());
|
||||
@ -30,31 +30,31 @@ auto SplitImpl(Future<Ts...> *future)
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
auto SplitImpl(Future<Ts...> *future)
|
||||
-> enable_if_t<(sizeof...(Ts) == 0), Future<Ts...>> {
|
||||
auto
|
||||
SplitImpl(Future<Ts...> *future) -> enable_if_t<(sizeof...(Ts) == 0), Future<Ts...>>
|
||||
{
|
||||
Promise<Ts...> promise;
|
||||
auto rc = promise.GetFuture();
|
||||
|
||||
auto moved_promise = MakeMoveOnCopy(promise);
|
||||
*future =
|
||||
std::move(*future).Then([moved_promise](Boxed<Ts...> boxed) mutable {
|
||||
moved_promise->SetBoxed(boxed);
|
||||
});
|
||||
*future = std::move(*future).Then([moved_promise](Boxed<Ts...> boxed) mutable { moved_promise->SetBoxed(boxed); });
|
||||
|
||||
return rc;
|
||||
}
|
||||
}// namespace detail
|
||||
|
||||
template <typename... Ts, typename = enable_if_t<
|
||||
std::is_copy_constructible<Boxed<Ts...>>::value>>
|
||||
std::pair<Future<Ts...>, Future<Ts...>> Split(Future<Ts...> *future) {
|
||||
template<typename... Ts, typename = enable_if_t<std::is_copy_constructible<Boxed<Ts...>>::value>>
|
||||
std::pair<Future<Ts...>, Future<Ts...>>
|
||||
Split(Future<Ts...> *future)
|
||||
{
|
||||
auto new_f = detail::SplitImpl(future);
|
||||
return std::make_pair(std::move(new_f), std::move(*future));
|
||||
}
|
||||
|
||||
template <typename... Ts, typename = enable_if_t<
|
||||
std::is_copy_constructible<Boxed<Ts...>>::value>>
|
||||
std::pair<Future<Ts...>, Future<Ts...>> Split(Future<Ts...> &&future) {
|
||||
template<typename... Ts, typename = enable_if_t<std::is_copy_constructible<Boxed<Ts...>>::value>>
|
||||
std::pair<Future<Ts...>, Future<Ts...>>
|
||||
Split(Future<Ts...> &&future)
|
||||
{
|
||||
return Split(&future);
|
||||
}
|
||||
|
||||
|
@ -8,67 +8,80 @@
|
||||
|
||||
namespace tile {
|
||||
namespace future {
|
||||
template <typename... Ts> struct Types {};
|
||||
template<typename... Ts>
|
||||
struct Types {};
|
||||
|
||||
// Get type at the specify location
|
||||
template <typename T, std::size_t I> struct types_at;
|
||||
template <typename T, typename... Ts> struct types_at<Types<T, Ts...>, 0> {
|
||||
template<typename T, std::size_t I>
|
||||
struct types_at;
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
struct types_at<Types<T, Ts...>, 0> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<std::size_t I, typename T, typename... Ts>
|
||||
struct types_at<Types<T, Ts...>, I> : types_at<Types<Ts...>, I - 1> {};
|
||||
|
||||
template<typename T, std::size_t I>
|
||||
using types_at_t = typename types_at<T, I>::type;
|
||||
static_assert(std::is_same<types_at_t<Types<int, char, void>, 1>, char>::value,
|
||||
"Compiler support error: types_at_t");
|
||||
static_assert(std::is_same<types_at_t<Types<int, char, void>, 1>, char>::value, "Compiler support error: types_at_t");
|
||||
|
||||
template <typename... Ts> struct types_cat;
|
||||
template<typename... Ts>
|
||||
struct types_cat;
|
||||
|
||||
template <> struct types_cat<> {
|
||||
template<>
|
||||
struct types_cat<> {
|
||||
using type = Types<>;
|
||||
};
|
||||
template <typename... Ts> struct types_cat<Types<Ts...>> {
|
||||
|
||||
template<typename... Ts>
|
||||
struct types_cat<Types<Ts...>> {
|
||||
using type = Types<Ts...>;
|
||||
};
|
||||
|
||||
template<typename... Ts, typename... Us, typename... Vs>
|
||||
struct types_cat<Types<Ts...>, Types<Us...>, Vs...>
|
||||
: types_cat<Types<Ts..., Us...>, Vs...> {};
|
||||
struct types_cat<Types<Ts...>, Types<Us...>, Vs...> : types_cat<Types<Ts..., Us...>, Vs...> {};
|
||||
|
||||
template <typename... Ts> using types_cat_t = typename types_cat<Ts...>::type;
|
||||
template<typename... Ts>
|
||||
using types_cat_t = typename types_cat<Ts...>::type;
|
||||
|
||||
template<typename T, typename U>
|
||||
struct types_contains;
|
||||
|
||||
template<typename U>
|
||||
struct types_contains<Types<>, U> : std::false_type {};
|
||||
|
||||
template <typename T, typename U> struct types_contains;
|
||||
template <typename U> struct types_contains<Types<>, U> : std::false_type {};
|
||||
template<typename T, typename U>
|
||||
struct types_contains<Types<T>, U> : std::is_same<T, U> {};
|
||||
|
||||
template<typename T, typename U, typename... Ts>
|
||||
struct types_contains<Types<T, Ts...>, U>
|
||||
: std::conditional<std::is_same<T, U>::value, std::true_type,
|
||||
types_contains<Types<Ts...>, U>>::type {};
|
||||
: std::conditional<std::is_same<T, U>::value, std::true_type, types_contains<Types<Ts...>, U>>::type {};
|
||||
|
||||
static_assert(types_contains<Types<int, char>, char>::value, "");
|
||||
static_assert(!types_contains<Types<int, char>, char *>::value, "");
|
||||
|
||||
// Erase all occurrance of give type from Types<...>
|
||||
template <typename T, typename U> struct types_erase;
|
||||
template <typename U> struct types_erase<Types<>, U> {
|
||||
template<typename T, typename U>
|
||||
struct types_erase;
|
||||
|
||||
template<typename U>
|
||||
struct types_erase<Types<>, U> {
|
||||
using type = Types<>;
|
||||
};
|
||||
|
||||
template<typename T, typename U, typename... Ts>
|
||||
struct types_erase<Types<T, Ts...>, U>
|
||||
: types_cat<internal::conditional_t<!std::is_same<T, U>::value, Types<T>,
|
||||
Types<>>,
|
||||
: types_cat<internal::conditional_t<!std::is_same<T, U>::value, Types<T>, Types<>>,
|
||||
typename types_erase<Types<Ts...>, U>::type> {};
|
||||
|
||||
template<typename T, typename U>
|
||||
using types_erase_t = typename types_erase<T, U>::type;
|
||||
|
||||
static_assert(std::is_same<types_erase_t<Types<int, void, char>, void>,
|
||||
Types<int, char>>::value,
|
||||
"");
|
||||
static_assert(std::is_same<types_erase_t<Types<int, void, char>, void>, Types<int, char>>::value, "");
|
||||
static_assert(std::is_same<types_erase_t<Types<>, void>, Types<>>::value, "");
|
||||
static_assert(std::is_same<types_erase_t<Types<int, char>, void>,
|
||||
Types<int, char>>::value,
|
||||
"");
|
||||
static_assert(std::is_same<types_erase_t<Types<int, char>, void>, Types<int, char>>::value, "");
|
||||
|
||||
}// namespace future
|
||||
}// namespace tile
|
||||
|
@ -21,18 +21,18 @@ namespace tile {
|
||||
namespace future {
|
||||
namespace detail {
|
||||
template<typename LockType, typename DurationOrTimePoint>
|
||||
auto WaitLockByCondVar(CondVar &cv, LockType &lk,
|
||||
const DurationOrTimePoint &timeout)
|
||||
-> enable_if_t<future::detail::is_duration<DurationOrTimePoint>::value,
|
||||
bool> {
|
||||
auto
|
||||
WaitLockByCondVar(CondVar &cv, LockType &lk, const DurationOrTimePoint &timeout)
|
||||
-> enable_if_t<future::detail::is_duration<DurationOrTimePoint>::value, bool>
|
||||
{
|
||||
return cv.WaitFor(lk, timeout);
|
||||
}
|
||||
|
||||
template<typename LockType, typename DurationOrTimePoint>
|
||||
auto WaitLockByCondVar(CondVar &cv, LockType &lk,
|
||||
const DurationOrTimePoint &timeout)
|
||||
-> enable_if_t<future::detail::is_time_point<DurationOrTimePoint>::value,
|
||||
bool> {
|
||||
auto
|
||||
WaitLockByCondVar(CondVar &cv, LockType &lk, const DurationOrTimePoint &timeout)
|
||||
-> enable_if_t<future::detail::is_time_point<DurationOrTimePoint>::value, bool>
|
||||
{
|
||||
return cv.WaitUntil(lk, timeout);
|
||||
}
|
||||
|
||||
@ -42,38 +42,43 @@ auto BlockingTryGet(Future<Ts...> &&future, const DurationOrTimePoint &timeout)
|
||||
|
||||
template<typename... Ts, typename DurationOrTimePoint>
|
||||
auto BlockingTryGet(Future<Ts...> &&future, const DurationOrTimePoint &timeout)
|
||||
-> enable_if_t<!std::is_void<unboxed_type_t<Ts...>>::value,
|
||||
std::optional<unboxed_type_t<Ts...>>>;
|
||||
-> enable_if_t<!std::is_void<unboxed_type_t<Ts...>>::value, std::optional<unboxed_type_t<Ts...>>>;
|
||||
|
||||
template<typename... Ts>
|
||||
auto BlockingGetPreservingErrors(Future<Ts...> &&future) -> Boxed<Ts...>;
|
||||
|
||||
template<typename... Ts, typename DurationOrTimePoint>
|
||||
auto BlockingTryGetPreservingErrors(Future<Ts...> &&future,
|
||||
const DurationOrTimePoint &timeout)
|
||||
-> std::optional<Boxed<Ts...>>;
|
||||
const DurationOrTimePoint &timeout) -> std::optional<Boxed<Ts...>>;
|
||||
|
||||
}// namespace detail
|
||||
|
||||
template <typename... Ts> Future<decay_t<Ts>...> MakeReadyFuture(Ts &&...args);
|
||||
template<typename... Ts>
|
||||
Future<decay_t<Ts>...> MakeReadyFuture(Ts &&...args);
|
||||
|
||||
template<typename... Ts>
|
||||
auto BlockingGet(Future<Ts...> &&future) -> unboxed_type_t<Ts...>;
|
||||
|
||||
template<typename... Ts>
|
||||
auto BlockingGet(Future<Ts...> *future) -> unboxed_type_t<Ts...> {
|
||||
auto
|
||||
BlockingGet(Future<Ts...> *future) -> unboxed_type_t<Ts...>
|
||||
{
|
||||
return BlockingGet(std::move(*future));
|
||||
}
|
||||
|
||||
template<typename... Ts, typename DurationOrTimePoint>
|
||||
auto BlockingTryGet(Future<Ts...> future, const DurationOrTimePoint &timeout)
|
||||
-> future::detail::optional_or_bool_t<unboxed_type_t<Ts...>> {
|
||||
auto
|
||||
BlockingTryGet(Future<Ts...> future,
|
||||
const DurationOrTimePoint &timeout) -> future::detail::optional_or_bool_t<unboxed_type_t<Ts...>>
|
||||
{
|
||||
return future::detail::BlockingTryGet(std::move(future), timeout);
|
||||
}
|
||||
|
||||
template<typename... Ts, typename DurationOrTimePoint>
|
||||
auto BlockingTryGet(Future<Ts...> *future, const DurationOrTimePoint &timeout)
|
||||
-> detail::optional_or_bool_t<unboxed_type_t<Ts...>> {
|
||||
auto
|
||||
BlockingTryGet(Future<Ts...> *future,
|
||||
const DurationOrTimePoint &timeout) -> detail::optional_or_bool_t<unboxed_type_t<Ts...>>
|
||||
{
|
||||
return BlockingTryGet(std::move(*future), timeout);
|
||||
}
|
||||
|
||||
@ -84,7 +89,9 @@ namespace tile {
|
||||
namespace future {
|
||||
namespace detail {
|
||||
template<typename... Ts>
|
||||
auto BlockingGetPreservingErrors(Future<Ts...> &&future) -> Boxed<Ts...> {
|
||||
auto
|
||||
BlockingGetPreservingErrors(Future<Ts...> &&future) -> Boxed<Ts...>
|
||||
{
|
||||
Mutex lock;
|
||||
CondVar cv;
|
||||
std::optional<Boxed<Ts...>> receiver;
|
||||
@ -101,25 +108,28 @@ auto BlockingGetPreservingErrors(Future<Ts...> &&future) -> Boxed<Ts...> {
|
||||
}
|
||||
|
||||
template<typename... Ts, typename DurationOrTimePoint>
|
||||
auto BlockingTryGet(Future<Ts...> &&future, const DurationOrTimePoint &timeout)
|
||||
-> enable_if_t<std::is_void<unboxed_type_t<Ts...>>::value, bool> {
|
||||
auto
|
||||
BlockingTryGet(Future<Ts...> &&future,
|
||||
const DurationOrTimePoint &timeout) -> enable_if_t<std::is_void<unboxed_type_t<Ts...>>::value, bool>
|
||||
{
|
||||
return !!BlockingTryGetPreservingErrors(std::move(future), timeout);
|
||||
}
|
||||
|
||||
template<typename... Ts, typename DurationOrTimePoint>
|
||||
auto BlockingTryGet(Future<Ts...> &&future, const DurationOrTimePoint &timeout)
|
||||
-> enable_if_t<!std::is_void<unboxed_type_t<Ts...>>::value,
|
||||
std::optional<unboxed_type_t<Ts...>>> {
|
||||
auto
|
||||
BlockingTryGet(Future<Ts...> &&future, const DurationOrTimePoint &timeout)
|
||||
-> enable_if_t<!std::is_void<unboxed_type_t<Ts...>>::value, std::optional<unboxed_type_t<Ts...>>>
|
||||
{
|
||||
auto rc = BlockingTryGetPreservingErrors(std::move(future), timeout);
|
||||
if (rc) {
|
||||
return std::move(rc->Get());
|
||||
}
|
||||
if (rc) { return std::move(rc->Get()); }
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename... Ts, typename DurationOrTimePoint>
|
||||
auto BlockingTryGetPreservingErrors(Future<Ts...> &&future,
|
||||
const DurationOrTimePoint &timeout)
|
||||
-> std::optional<Boxed<Ts...>> {
|
||||
auto
|
||||
BlockingTryGetPreservingErrors(Future<Ts...> &&future,
|
||||
const DurationOrTimePoint &timeout) -> std::optional<Boxed<Ts...>>
|
||||
{
|
||||
struct State {
|
||||
CondVar cv;
|
||||
Mutex lock;
|
||||
@ -141,12 +151,17 @@ auto BlockingTryGetPreservingErrors(Future<Ts...> &&future,
|
||||
|
||||
}// namespace detail
|
||||
|
||||
template <typename... Ts> Future<decay_t<Ts>...> MakeReadyFuture(Ts &&...args) {
|
||||
template<typename... Ts>
|
||||
Future<decay_t<Ts>...>
|
||||
MakeReadyFuture(Ts &&...args)
|
||||
{
|
||||
return Future<decay_t<Ts>...>(futurize_values, std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
auto BlockingGet(Future<Ts...> &&future) -> unboxed_type_t<Ts...> {
|
||||
auto
|
||||
BlockingGet(Future<Ts...> &&future) -> unboxed_type_t<Ts...>
|
||||
{
|
||||
return future::detail::BlockingGetPreservingErrors(std::move(future)).Get();
|
||||
}
|
||||
|
||||
|
@ -16,17 +16,17 @@ namespace future {
|
||||
namespace detail {
|
||||
|
||||
struct Flatten {
|
||||
template <typename T,
|
||||
typename R = internal::conditional_t<
|
||||
(std::tuple_size<T>::value >= 2), std::tuple<T &&>, T>>
|
||||
static auto Rebox(T &&t) -> R {
|
||||
template<typename T, typename R = internal::conditional_t<(std::tuple_size<T>::value >= 2), std::tuple<T &&>, T>>
|
||||
static auto Rebox(T &&t) -> R
|
||||
{
|
||||
return R(std::forward<T>(t));
|
||||
};
|
||||
|
||||
template <typename T, int... Is,
|
||||
typename R = decltype(std::tuple_cat(
|
||||
Rebox(std::move(std::get<Is>(std::declval<T>()).GetRaw()))...))>
|
||||
auto operator()(T &&t, index_sequence<Is...>) -> R {
|
||||
template<typename T,
|
||||
int... Is,
|
||||
typename R = decltype(std::tuple_cat(Rebox(std::move(std::get<Is>(std::declval<T>()).GetRaw()))...))>
|
||||
auto operator()(T &&t, index_sequence<Is...>) -> R
|
||||
{
|
||||
return std::tuple_cat(Rebox(std::move(std::get<Is>(t).GetRaw()))...);
|
||||
}
|
||||
};
|
||||
@ -35,8 +35,7 @@ template <typename... Ts>
|
||||
struct WhenAllImpl : public std::enable_shared_from_this<WhenAllImpl<Ts...>> {
|
||||
struct Context {
|
||||
Promise<as_boxed_t<Ts>...> promise;
|
||||
std::tuple<as_boxed_t<Ts>...> receiver{
|
||||
future::detail::RetrieveBoxed<as_boxed_t<Ts>>()...};
|
||||
std::tuple<as_boxed_t<Ts>...> receiver{future::detail::RetrieveBoxed<as_boxed_t<Ts>>()...};
|
||||
std::atomic<std::size_t> left{sizeof...(Ts)};
|
||||
};
|
||||
|
||||
@ -44,56 +43,51 @@ struct WhenAllImpl : public std::enable_shared_from_this<WhenAllImpl<Ts...>> {
|
||||
|
||||
// template <int I, typename...> void ForEach() {}
|
||||
|
||||
template <int I, typename T> void ForEach(T &&future) {
|
||||
template<int I, typename T>
|
||||
void ForEach(T &&future)
|
||||
{
|
||||
auto self = this->shared_from_this();
|
||||
std::move(future).Then(
|
||||
[self, this](
|
||||
as_boxed_t<internal::remove_reference_t<decltype(future)>> boxed) {
|
||||
std::move(future).Then([self, this](as_boxed_t<internal::remove_reference_t<decltype(future)>> boxed) {
|
||||
std::get<I>(context->receiver) = std::move(boxed);
|
||||
if (context->left.fetch_sub(1) == 1) {
|
||||
context->promise.SetValue(std::move(context->receiver));
|
||||
}
|
||||
if (context->left.fetch_sub(1) == 1) { context->promise.SetValue(std::move(context->receiver)); }
|
||||
});
|
||||
}
|
||||
|
||||
template<int I, typename T, typename... Us>
|
||||
void ForEach(T &&future, Us &&...futures) {
|
||||
void ForEach(T &&future, Us &&...futures)
|
||||
{
|
||||
ForEach<I>(std::move(future));
|
||||
ForEach<I + 1>(std::move(futures)...);
|
||||
}
|
||||
|
||||
template<
|
||||
typename = enable_if_t<(sizeof...(Ts) > 0) && is_futures<Ts...>::value &&
|
||||
are_rvalue_refs<Ts &&...>::value>>
|
||||
auto WhenAllPreservingErrors(Ts &&...futures) -> Future<as_boxed_t<Ts>...> {
|
||||
typename = enable_if_t<(sizeof...(Ts) > 0) && is_futures<Ts...>::value && are_rvalue_refs<Ts &&...>::value>>
|
||||
auto WhenAllPreservingErrors(Ts &&...futures) -> Future<as_boxed_t<Ts>...>
|
||||
{
|
||||
context = std::make_shared<Context>();
|
||||
ForEach<0>(std::move(futures)...);
|
||||
return context->promise.GetFuture();
|
||||
}
|
||||
|
||||
template<
|
||||
typename = enable_if_t<(sizeof...(Ts) > 0) && is_futures<Ts...>::value &&
|
||||
are_rvalue_refs<Ts &&...>::value>,
|
||||
typename R = rebind_t<
|
||||
types_erase_t<Types<decltype(BlockingGet(std::declval<Ts>()))...>,
|
||||
void>,
|
||||
Future>>
|
||||
auto WhenAll(Ts &&...futures) -> R {
|
||||
typename = enable_if_t<(sizeof...(Ts) > 0) && is_futures<Ts...>::value && are_rvalue_refs<Ts &&...>::value>,
|
||||
typename R = rebind_t<types_erase_t<Types<decltype(BlockingGet(std::declval<Ts>()))...>, void>, Future>>
|
||||
auto WhenAll(Ts &&...futures) -> R
|
||||
{
|
||||
// Future<...>, Future<...>, ...
|
||||
return WhenAllPreservingErrors(std::move(futures)...)
|
||||
.Then([](Boxed<as_boxed_t<Ts>...> boxes) -> R {
|
||||
return WhenAllPreservingErrors(std::move(futures)...).Then([](Boxed<as_boxed_t<Ts>...> boxes) -> R {
|
||||
// std::tuple<Boxed<...>, Boxed<...>, ...>
|
||||
auto &&raw = boxes.GetRaw();
|
||||
|
||||
auto &&flattened =
|
||||
detail::Flatten()(raw, index_sequence_for<Ts...>());
|
||||
auto &&flattened = detail::Flatten()(raw, index_sequence_for<Ts...>());
|
||||
|
||||
return R(futurize_tuple, std::move(flattened));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template <template <typename...> class C, typename... Ts> struct WhenAllCImpl {
|
||||
template<template<typename...> class C, typename... Ts>
|
||||
struct WhenAllCImpl {
|
||||
// Collection of futures
|
||||
struct Context {
|
||||
Promise<C<Boxed<Ts...>>> promise;
|
||||
@ -101,12 +95,10 @@ template <template <typename...> class C, typename... Ts> struct WhenAllCImpl {
|
||||
std::atomic<std::size_t> left;
|
||||
};
|
||||
|
||||
static auto WhenAllPreservingErrors(C<Future<Ts...>> &&futures)
|
||||
-> Future<C<Boxed<Ts...>>> {
|
||||
static auto WhenAllPreservingErrors(C<Future<Ts...>> &&futures) -> Future<C<Boxed<Ts...>>>
|
||||
{
|
||||
using NewFuture = Future<C<Boxed<Ts...>>>;
|
||||
if (futures.empty()) {
|
||||
return NewFuture(futurize_values, C<Boxed<Ts...>>());
|
||||
}
|
||||
if (futures.empty()) { return NewFuture(futurize_values, C<Boxed<Ts...>>()); }
|
||||
|
||||
auto context = std::make_shared<Context>();
|
||||
context->values.reserve(futures.size());
|
||||
@ -115,23 +107,19 @@ template <template <typename...> class C, typename... Ts> struct WhenAllCImpl {
|
||||
for (std::size_t index = 0; index != futures.size(); ++index) {
|
||||
std::move(futures[index]).Then([=](Boxed<Ts...> boxed) mutable {
|
||||
context->values[index] = std::move(boxed);
|
||||
if (context->left.fetch_sub(1) == 1) {
|
||||
context->promise.SetValue(std::move(context->values));
|
||||
}
|
||||
if (context->left.fetch_sub(1) == 1) { context->promise.SetValue(std::move(context->values)); }
|
||||
});
|
||||
}
|
||||
|
||||
return context->promise.GetFuture();
|
||||
}
|
||||
|
||||
template <typename R = internal::conditional_t<
|
||||
std::is_void<unboxed_type_t<Ts...>>::value, Future<>,
|
||||
Future<C<unboxed_type_t<Ts...>>>>>
|
||||
static auto WhenAll(C<Future<Ts...>> &&futures)
|
||||
-> enable_if_t<!std::is_void<unboxed_type_t<Ts...>>::value, R> {
|
||||
template<typename R = internal::
|
||||
conditional_t<std::is_void<unboxed_type_t<Ts...>>::value, Future<>, Future<C<unboxed_type_t<Ts...>>>>>
|
||||
static auto WhenAll(C<Future<Ts...>> &&futures) -> enable_if_t<!std::is_void<unboxed_type_t<Ts...>>::value, R>
|
||||
{
|
||||
|
||||
return WhenAllPreservingErrors(std::move(futures))
|
||||
.Then([](C<Boxed<Ts...>> boxed_values) {
|
||||
return WhenAllPreservingErrors(std::move(futures)).Then([](C<Boxed<Ts...>> boxed_values) {
|
||||
C<unboxed_type_t<Ts...>> result;
|
||||
result.reserve(boxed_values.size());
|
||||
for (auto &&e : boxed_values) {
|
||||
@ -142,13 +130,12 @@ template <template <typename...> class C, typename... Ts> struct WhenAllCImpl {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
template <typename R = internal::conditional_t<
|
||||
std::is_void<unboxed_type_t<Ts...>>::value, Future<>,
|
||||
Future<C<unboxed_type_t<Ts...>>>>>
|
||||
static auto WhenAll(C<Future<Ts...>> &&futures)
|
||||
-> enable_if_t<std::is_void<unboxed_type_t<Ts...>>::value, R> {
|
||||
return WhenAllPreservingErrors(std::move(futures))
|
||||
.Then([](C<Boxed<Ts...>> boxed_values) {
|
||||
|
||||
template<typename R = internal::
|
||||
conditional_t<std::is_void<unboxed_type_t<Ts...>>::value, Future<>, Future<C<unboxed_type_t<Ts...>>>>>
|
||||
static auto WhenAll(C<Future<Ts...>> &&futures) -> enable_if_t<std::is_void<unboxed_type_t<Ts...>>::value, R>
|
||||
{
|
||||
return WhenAllPreservingErrors(std::move(futures)).Then([](C<Boxed<Ts...>> boxed_values) {
|
||||
(void) boxed_values;
|
||||
return;
|
||||
});
|
||||
@ -161,32 +148,37 @@ template <template <typename...> class C, typename... Ts> struct WhenAllCImpl {
|
||||
|
||||
namespace tile {
|
||||
namespace future {
|
||||
template <typename... Ts,
|
||||
typename = enable_if_t<tile::future::is_futures<Ts...>::value>>
|
||||
auto WhenAll(Ts &&...futures)
|
||||
-> decltype(std::declval<future::detail::WhenAllImpl<Ts...>>().WhenAll(
|
||||
std::move(futures)...)) {
|
||||
template<typename... Ts, typename = enable_if_t<tile::future::is_futures<Ts...>::value>>
|
||||
auto
|
||||
WhenAll(Ts &&...futures) -> decltype(std::declval<future::detail::WhenAllImpl<Ts...>>().WhenAll(std::move(futures)...))
|
||||
{
|
||||
auto impl = std::make_shared<future::detail::WhenAllImpl<Ts...>>();
|
||||
return impl->WhenAll(std::move(futures)...);
|
||||
}
|
||||
template <class... Ts,
|
||||
class = enable_if_t<tile::future::is_futures<Ts...>::value>>
|
||||
auto WhenAll(Ts *...futures) -> decltype(WhenAll(std::move(*futures)...)) {
|
||||
|
||||
template<class... Ts, class = enable_if_t<tile::future::is_futures<Ts...>::value>>
|
||||
auto
|
||||
WhenAll(Ts *...futures) -> decltype(WhenAll(std::move(*futures)...))
|
||||
{
|
||||
return WhenAll(std::move(*futures)...);
|
||||
}
|
||||
template <template <typename...> class C, class... Ts,
|
||||
typename R = internal::conditional_t<
|
||||
std::is_void<tile::future::unboxed_type_t<Ts...>>::value,
|
||||
|
||||
template<template<typename...> class C,
|
||||
class... Ts,
|
||||
typename R = internal::conditional_t<std::is_void<tile::future::unboxed_type_t<Ts...>>::value,
|
||||
tile::future::Future<>,
|
||||
tile::future::Future<C<tile::future::unboxed_type_t<Ts...>>>>>
|
||||
|
||||
auto WhenAll(C<tile::future::Future<Ts...>> &&futures) -> R {
|
||||
auto
|
||||
WhenAll(C<tile::future::Future<Ts...>> &&futures) -> R
|
||||
{
|
||||
return future::detail::WhenAllCImpl<C, Ts...>::WhenAll(std::move(futures));
|
||||
}
|
||||
|
||||
template<template<typename...> class C, class... Ts>
|
||||
auto WhenAll(C<tile::future::Future<Ts...>> *futures)
|
||||
-> decltype(WhenAll(std::move(*futures))) {
|
||||
auto
|
||||
WhenAll(C<tile::future::Future<Ts...>> *futures) -> decltype(WhenAll(std::move(*futures)))
|
||||
{
|
||||
return WhenAll(std::move(*futures));
|
||||
}
|
||||
}// namespace future
|
||||
|
@ -11,10 +11,10 @@
|
||||
namespace tile {
|
||||
namespace future {
|
||||
template<template<typename...> class C, typename... Ts>
|
||||
auto WhenAnyPreservingErrors(C<Future<Ts...>> &&futures)
|
||||
-> Future<std::size_t, Boxed<Ts...>> {
|
||||
TILE_CHECK(!futures.empty(),
|
||||
"Cannot wait for any future from an empty list.");
|
||||
auto
|
||||
WhenAnyPreservingErrors(C<Future<Ts...>> &&futures) -> Future<std::size_t, Boxed<Ts...>>
|
||||
{
|
||||
TILE_CHECK(!futures.empty(), "Cannot wait for any future from an empty list.");
|
||||
|
||||
struct Context {
|
||||
Promise<std::size_t, Boxed<Ts...>> promise;
|
||||
@ -25,43 +25,44 @@ auto WhenAnyPreservingErrors(C<Future<Ts...>> &&futures)
|
||||
|
||||
for (std::size_t index = 0; index < futures.size(); ++index) {
|
||||
std::move(futures[index]).Then([context, index](Boxed<Ts...> boxed) {
|
||||
if (!context->ever_satisfied.exchange(true)) {
|
||||
context->promise.SetValue(index, std::move(boxed));
|
||||
}
|
||||
if (!context->ever_satisfied.exchange(true)) { context->promise.SetValue(index, std::move(boxed)); }
|
||||
});
|
||||
}
|
||||
|
||||
return context->promise.GetFuture();
|
||||
}
|
||||
|
||||
template <template <typename...> class C, typename... Ts,
|
||||
typename R = internal::conditional_t<
|
||||
std::is_void<unboxed_type_t<Ts...>>::value, Future<std::size_t>,
|
||||
template<template<typename...> class C,
|
||||
typename... Ts,
|
||||
typename R = internal::conditional_t<std::is_void<unboxed_type_t<Ts...>>::value,
|
||||
Future<std::size_t>,
|
||||
Future<std::size_t, unboxed_type_t<Ts...>>>>
|
||||
auto WhenAny(C<Future<Ts...>> &&futures)
|
||||
-> enable_if_t<(sizeof...(Ts) == 0), R> {
|
||||
return WhenAnyPreservingErrors(std::move(futures))
|
||||
.Then([](std::size_t index, Boxed<Ts...> values) {
|
||||
auto
|
||||
WhenAny(C<Future<Ts...>> &&futures) -> enable_if_t<(sizeof...(Ts) == 0), R>
|
||||
{
|
||||
return WhenAnyPreservingErrors(std::move(futures)).Then([](std::size_t index, Boxed<Ts...> values) {
|
||||
(void) values;
|
||||
return index;
|
||||
});
|
||||
}
|
||||
|
||||
template <template <typename...> class C, typename... Ts,
|
||||
typename R = internal::conditional_t<
|
||||
std::is_void<unboxed_type_t<Ts...>>::value, Future<std::size_t>,
|
||||
template<template<typename...> class C,
|
||||
typename... Ts,
|
||||
typename R = internal::conditional_t<std::is_void<unboxed_type_t<Ts...>>::value,
|
||||
Future<std::size_t>,
|
||||
Future<std::size_t, unboxed_type_t<Ts...>>>>
|
||||
auto WhenAny(C<Future<Ts...>> &&futures)
|
||||
-> enable_if_t<(sizeof...(Ts) != 0), R> {
|
||||
return WhenAnyPreservingErrors(std::move(futures))
|
||||
.Then([](std::size_t index, Boxed<Ts...> values) {
|
||||
auto
|
||||
WhenAny(C<Future<Ts...>> &&futures) -> enable_if_t<(sizeof...(Ts) != 0), R>
|
||||
{
|
||||
return WhenAnyPreservingErrors(std::move(futures)).Then([](std::size_t index, Boxed<Ts...> values) {
|
||||
return R(futurize_values, index, values.Get());
|
||||
});
|
||||
}
|
||||
|
||||
template<template<class...> class C, class... Ts>
|
||||
auto WhenAny(C<Future<Ts...>> *futures)
|
||||
-> decltype(WhenAny(std::move(*futures))) {
|
||||
auto
|
||||
WhenAny(C<Future<Ts...>> *futures) -> decltype(WhenAny(std::move(*futures)))
|
||||
{
|
||||
return WhenAny(std::move(*futures));
|
||||
}
|
||||
|
||||
|
@ -10,26 +10,32 @@
|
||||
namespace tile {
|
||||
namespace detail {
|
||||
struct HandleDeleter {
|
||||
void operator()(int fd) const noexcept {
|
||||
if (fd == 0 || fd == -1) {
|
||||
return;
|
||||
}
|
||||
void operator()(int fd) const noexcept
|
||||
{
|
||||
if (fd == 0 || fd == -1) { return; }
|
||||
TILE_PCHECK(::close(fd) == 0);
|
||||
}
|
||||
};
|
||||
|
||||
namespace handle {
|
||||
template<typename T, typename... Args>
|
||||
constexpr T GetFirstArg(T first, Args...) {
|
||||
constexpr T
|
||||
GetFirstArg(T first, Args...)
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
template <typename T> constexpr bool NotEqualToAnd(T val, T first) {
|
||||
template<typename T>
|
||||
constexpr bool
|
||||
NotEqualToAnd(T val, T first)
|
||||
{
|
||||
return val != first;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
constexpr bool NotEqualToAnd(T val, T first, Args... args) {
|
||||
constexpr bool
|
||||
NotEqualToAnd(T val, T first, Args... args)
|
||||
{
|
||||
return val != first && NotEqualToAnd(val, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@ -37,23 +43,24 @@ constexpr bool NotEqualToAnd(T val, T first, Args... args) {
|
||||
|
||||
template<typename T, typename Deleter, T... kInvalidHandles>
|
||||
class GenericHandle : private Deleter {
|
||||
static constexpr T kDefaultInvalid =
|
||||
::tile::detail::handle::GetFirstArg(kInvalidHandles...);
|
||||
static constexpr T kDefaultInvalid = ::tile::detail::handle::GetFirstArg(kInvalidHandles...);
|
||||
|
||||
public:
|
||||
GenericHandle() = default;
|
||||
|
||||
constexpr explicit GenericHandle(T handle) noexcept : handle_(handle) {}
|
||||
|
||||
~GenericHandle() { Reset(); }
|
||||
|
||||
GenericHandle(GenericHandle &&h) noexcept {
|
||||
GenericHandle(GenericHandle &&h) noexcept
|
||||
{
|
||||
handle_ = h.handle_;
|
||||
h.handle_ = kDefaultInvalid;
|
||||
}
|
||||
|
||||
GenericHandle &operator=(GenericHandle &&h) noexcept {
|
||||
if (&h == this) {
|
||||
return *this;
|
||||
}
|
||||
GenericHandle &operator=(GenericHandle &&h) noexcept
|
||||
{
|
||||
if (&h == this) { return *this; }
|
||||
|
||||
handle_ = h.handle_;
|
||||
h.handle_ = kDefaultInvalid;
|
||||
@ -64,21 +71,24 @@ public:
|
||||
GenericHandle &operator=(const GenericHandle &) = delete;
|
||||
|
||||
T *Retrieve() noexcept { return &handle_; }
|
||||
|
||||
constexpr T Get() const noexcept { return handle_; }
|
||||
constexpr explicit operator bool() const noexcept {
|
||||
|
||||
constexpr explicit operator bool() const noexcept
|
||||
{
|
||||
return ::tile::detail::handle::NotEqualToAnd(handle_, kInvalidHandles...);
|
||||
}
|
||||
|
||||
TILE_NODISCARD T Leak() noexcept {
|
||||
TILE_NODISCARD T Leak() noexcept
|
||||
{
|
||||
int rc = handle_;
|
||||
handle_ = kDefaultInvalid;
|
||||
return rc;
|
||||
}
|
||||
|
||||
void Reset(T new_value = kDefaultInvalid) noexcept {
|
||||
if (operator bool()) {
|
||||
Deleter::operator()(handle_);
|
||||
}
|
||||
void Reset(T new_value = kDefaultInvalid) noexcept
|
||||
{
|
||||
if (operator bool()) { Deleter::operator()(handle_); }
|
||||
handle_ = new_value;
|
||||
}
|
||||
|
||||
@ -87,6 +97,7 @@ private:
|
||||
};
|
||||
|
||||
}// namespace detail
|
||||
|
||||
using Handle = detail::GenericHandle<int, detail::HandleDeleter, -1, 0>;
|
||||
}// namespace tile
|
||||
|
||||
|
@ -8,7 +8,8 @@
|
||||
|
||||
namespace tile {
|
||||
|
||||
TEST(Handle, InvalidValues) {
|
||||
TEST(Handle, InvalidValues)
|
||||
{
|
||||
Handle h(0), h2(-1), h3(1);
|
||||
ASSERT_FALSE(h);
|
||||
ASSERT_FALSE(h2);
|
||||
@ -17,7 +18,8 @@ TEST(Handle, InvalidValues) {
|
||||
(void) holder;
|
||||
}
|
||||
|
||||
TEST(Handle, ValidHandle) {
|
||||
TEST(Handle, ValidHandle)
|
||||
{
|
||||
int fd = 0;
|
||||
{
|
||||
Handle h(open("/dev/null", O_WRONLY));
|
||||
|
@ -8,21 +8,24 @@
|
||||
|
||||
namespace tile {
|
||||
namespace id_alloc {
|
||||
template <typename Traits> class AllocImpl;
|
||||
template<typename Traits>
|
||||
class AllocImpl;
|
||||
|
||||
template<typename Traits>
|
||||
inline auto Next() -> typename AllocImpl<Traits>::Type {
|
||||
inline auto
|
||||
Next() -> typename AllocImpl<Traits>::Type
|
||||
{
|
||||
return AllocImpl<Traits>::Next();
|
||||
};
|
||||
|
||||
template <typename Traits> class AllocImpl {
|
||||
template<typename Traits>
|
||||
class AllocImpl {
|
||||
public:
|
||||
using Type = typename Traits::Type;
|
||||
|
||||
static Type Next() {
|
||||
if (TILE_LIKELY(current < max)) {
|
||||
return current++;
|
||||
}
|
||||
static Type Next()
|
||||
{
|
||||
if (TILE_LIKELY(current < max)) { return current++; }
|
||||
|
||||
return SlowNext();
|
||||
}
|
||||
@ -46,7 +49,9 @@ template <typename Traits>
|
||||
thread_local typename Traits::Type AllocImpl<Traits>::max;
|
||||
|
||||
template<typename Traits>
|
||||
typename AllocImpl<Traits>::Type AllocImpl<Traits>::SlowNext() {
|
||||
typename AllocImpl<Traits>::Type
|
||||
AllocImpl<Traits>::SlowNext()
|
||||
{
|
||||
static_assert(Traits::kMIn < Traits::kMax, "");
|
||||
static_assert(Traits::kBatchSize > 0, "");
|
||||
static_assert(Traits::kBatchSize + Traits::kMin < Traits::kMax, "");
|
||||
@ -67,8 +72,7 @@ typename AllocImpl<Traits>::Type AllocImpl<Traits>::SlowNext() {
|
||||
next = read + Traits::kBatchSize;
|
||||
max = next;
|
||||
}
|
||||
} while (
|
||||
!global.compare_exchange_weak(read, next, std::memory_order_relaxed));
|
||||
} while (!global.compare_exchange_weak(read, next, std::memory_order_relaxed));
|
||||
|
||||
TILE_CHECK_GE(max, Traits::kMin);
|
||||
TILE_CHECK_LE(max, Traits::kMax);
|
||||
|
@ -10,19 +10,22 @@
|
||||
#include <utility>
|
||||
|
||||
DEFINE_int32(tile_background_task_host_workers_per_node, 4, "");
|
||||
DEFINE_int32(tile_background_task_host_workers_nice_value, 5,
|
||||
"Determines niceness of background host workers");
|
||||
DEFINE_int32(tile_background_task_host_workers_nice_value, 5, "Determines niceness of background host workers");
|
||||
|
||||
namespace tile {
|
||||
namespace internal {
|
||||
namespace {}// namespace
|
||||
|
||||
BackgroundTaskHost *BackgroundTaskHost::Instance() {
|
||||
BackgroundTaskHost *
|
||||
BackgroundTaskHost::Instance()
|
||||
{
|
||||
static NeverDestroyedSingleton<BackgroundTaskHost> background_task_host;
|
||||
return background_task_host.Get();
|
||||
}
|
||||
|
||||
void BackgroundTaskHost::Start() {
|
||||
void
|
||||
BackgroundTaskHost::Start()
|
||||
{
|
||||
auto logical_cpus = std::thread::hardware_concurrency();
|
||||
if (logical_cpus == 0) {
|
||||
logical_cpus = 1;
|
||||
@ -31,24 +34,26 @@ void BackgroundTaskHost::Start() {
|
||||
std::vector<int> affinity;
|
||||
|
||||
pools_.resize(1);
|
||||
pools_[0] = make_unique<ThreadPool>(
|
||||
logical_cpus, affinity,
|
||||
FLAGS_tile_background_task_host_workers_nice_value);
|
||||
pools_[0] = make_unique<ThreadPool>(logical_cpus, affinity, FLAGS_tile_background_task_host_workers_nice_value);
|
||||
}
|
||||
|
||||
void BackgroundTaskHost::Stop() {
|
||||
for (auto &&e : pools_) {
|
||||
e->Stop();
|
||||
}
|
||||
}
|
||||
void BackgroundTaskHost::Join() {
|
||||
for (auto &&e : pools_) {
|
||||
e->Join();
|
||||
void
|
||||
BackgroundTaskHost::Stop()
|
||||
{
|
||||
for (auto &&e : pools_) { e->Stop(); }
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundTaskHost::Join()
|
||||
{
|
||||
for (auto &&e : pools_) { e->Join(); }
|
||||
|
||||
pools_.clear();
|
||||
}
|
||||
void BackgroundTaskHost::Queue(std::function<void()> &&op) {
|
||||
|
||||
void
|
||||
BackgroundTaskHost::Queue(std::function<void()> &&op)
|
||||
{
|
||||
TILE_CHECK_GT(pools_.size(), 0);
|
||||
pools_[Random() % pools_.size()]->QueueJob(std::move(op));
|
||||
}
|
||||
|
@ -7,7 +7,8 @@
|
||||
namespace tile {
|
||||
namespace internal {
|
||||
|
||||
TEST(BackgroundTaskHost, All) {
|
||||
TEST(BackgroundTaskHost, All)
|
||||
{
|
||||
// BackgroundTaskHost::Instance()->Start();
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
int x = 1;
|
||||
|
@ -12,20 +12,18 @@ namespace tile {
|
||||
namespace internal {
|
||||
namespace detail {
|
||||
struct CaseInsensitiveHash {
|
||||
std::size_t operator()(Slice s) const noexcept {
|
||||
std::size_t operator()(Slice s) const noexcept
|
||||
{
|
||||
uint64_t hash = 5381;
|
||||
for (auto &&c : s) {
|
||||
hash += ((hash << 5) + hash) + ToLower(c);
|
||||
}
|
||||
for (auto &&c : s) { hash += ((hash << 5) + hash) + ToLower(c); }
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
struct CaseInsensitiveEqualTo {
|
||||
bool operator()(Slice x, Slice y) const noexcept {
|
||||
if (x.size() != y.size()) {
|
||||
return false;
|
||||
}
|
||||
bool operator()(Slice x, Slice y) const noexcept
|
||||
{
|
||||
if (x.size() != y.size()) { return false; }
|
||||
|
||||
return EqualsIgnoreCase(x, y);
|
||||
}
|
||||
@ -33,9 +31,7 @@ struct CaseInsensitiveEqualTo {
|
||||
}// namespace detail
|
||||
|
||||
template<typename K, typename V>
|
||||
using CaseInsensitiveHashMap =
|
||||
std::unordered_map<K, V, detail::CaseInsensitiveHash,
|
||||
detail::CaseInsensitiveEqualTo>;
|
||||
using CaseInsensitiveHashMap = std::unordered_map<K, V, detail::CaseInsensitiveHash, detail::CaseInsensitiveEqualTo>;
|
||||
}// namespace internal
|
||||
}// namespace tile
|
||||
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
namespace tile {
|
||||
namespace internal {
|
||||
TEST(CaseInsensitiveHashMap, Easy) {
|
||||
TEST(CaseInsensitiveHashMap, Easy)
|
||||
{
|
||||
CaseInsensitiveHashMap<std::string, std::string> m;
|
||||
|
||||
m["a"] = "1";
|
||||
|
@ -22,39 +22,42 @@ HttpPostMockHandler s_http_post_mock_handler;
|
||||
|
||||
HttpGetMockHandler s_http_get_mock_handler;
|
||||
|
||||
size_t HttpWriteCallback(char *ptr, size_t size, size_t nmemb, void *pstr) {
|
||||
size_t
|
||||
HttpWriteCallback(char *ptr, size_t size, size_t nmemb, void *pstr)
|
||||
{
|
||||
auto bytes = size * nmemb;
|
||||
// What ?
|
||||
static_cast<std::string *>(pstr)->append(ptr, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#define CURL_SHARE_SETOPT(handle, opt, val) \
|
||||
TILE_CHECK_EQ(CURLSHE_OK, curl_share_setopt(handle, opt, val))
|
||||
#define CURL_SHARE_SETOPT(handle, opt, val) TILE_CHECK_EQ(CURLSHE_OK, curl_share_setopt(handle, opt, val))
|
||||
|
||||
static NeverDestroyed<std::array<Mutex, CURL_LOCK_DATA_LAST>>
|
||||
share_handle_mutex;
|
||||
static NeverDestroyed<std::array<Mutex, CURL_LOCK_DATA_LAST>> share_handle_mutex;
|
||||
|
||||
void AcquireLockOfShareHandle(CURL *handle, curl_lock_data data,
|
||||
curl_lock_access access, void *userptr) {
|
||||
void
|
||||
AcquireLockOfShareHandle(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr)
|
||||
{
|
||||
(*share_handle_mutex)[data].Lock();
|
||||
}
|
||||
void ReleaseLockOfShareHandle(CURL *handle, curl_lock_data data,
|
||||
void *userptr) {
|
||||
|
||||
void
|
||||
ReleaseLockOfShareHandle(CURL *handle, curl_lock_data data, void *userptr)
|
||||
{
|
||||
(*share_handle_mutex)[data].Unlock();
|
||||
}
|
||||
|
||||
CURLSH *GetCurlShareHandle() {
|
||||
CURLSH *
|
||||
GetCurlShareHandle()
|
||||
{
|
||||
using Handle = std::unique_ptr<CURLSH, decltype(&curl_share_cleanup)>;
|
||||
static NeverDestroyed<Handle> share([] {
|
||||
Handle share{curl_share_init(), &curl_share_cleanup};
|
||||
CURL_SHARE_SETOPT(share.get(), CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
|
||||
CURL_SHARE_SETOPT(share.get(), CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
|
||||
|
||||
CURL_SHARE_SETOPT(share.get(), CURLSHOPT_LOCKFUNC,
|
||||
AcquireLockOfShareHandle);
|
||||
CURL_SHARE_SETOPT(share.get(), CURLSHOPT_UNLOCKFUNC,
|
||||
ReleaseLockOfShareHandle);
|
||||
CURL_SHARE_SETOPT(share.get(), CURLSHOPT_LOCKFUNC, AcquireLockOfShareHandle);
|
||||
CURL_SHARE_SETOPT(share.get(), CURLSHOPT_UNLOCKFUNC, ReleaseLockOfShareHandle);
|
||||
|
||||
return share;
|
||||
}());
|
||||
@ -62,26 +65,24 @@ CURLSH *GetCurlShareHandle() {
|
||||
return share->get();
|
||||
}
|
||||
|
||||
#define CURL_SETOPT(handle, opt, val) \
|
||||
TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(handle, opt, val))
|
||||
#define CURL_SETOPT(handle, opt, val) TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(handle, opt, val))
|
||||
|
||||
}// namespace
|
||||
|
||||
std::expected<std::string, int>
|
||||
HttpPost(const std::string &uri, const std::vector<std::string> &headers,
|
||||
const std::string &body, std::chrono::nanoseconds timeout) {
|
||||
if (s_http_post_mock_handler) {
|
||||
return s_http_post_mock_handler(uri, headers, body, timeout);
|
||||
}
|
||||
HttpPost(const std::string &uri,
|
||||
const std::vector<std::string> &headers,
|
||||
const std::string &body,
|
||||
std::chrono::nanoseconds timeout)
|
||||
{
|
||||
if (s_http_post_mock_handler) { return s_http_post_mock_handler(uri, headers, body, timeout); }
|
||||
|
||||
std::string result;
|
||||
curl_slist *hdrs = curl_slist_append(nullptr, "Expect:");
|
||||
ScopedDeferred _{[&] { curl_slist_free_all(hdrs); }};
|
||||
for (const auto &header : headers) {
|
||||
hdrs = curl_slist_append(hdrs, header.c_str());
|
||||
}
|
||||
for (const auto &header : headers) { hdrs = curl_slist_append(hdrs, header.c_str()); }
|
||||
|
||||
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl{curl_easy_init(),
|
||||
&curl_easy_cleanup};
|
||||
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl{curl_easy_init(), &curl_easy_cleanup};
|
||||
|
||||
CURL_SETOPT(curl.get(), CURLOPT_URL, uri.c_str());
|
||||
CURL_SETOPT(curl.get(), CURLOPT_POSTFIELDS, body.c_str());
|
||||
@ -90,62 +91,55 @@ HttpPost(const std::string &uri, const std::vector<std::string> &headers,
|
||||
CURL_SETOPT(curl.get(), CURLOPT_WRITEFUNCTION, HttpWriteCallback);
|
||||
CURL_SETOPT(curl.get(), CURLOPT_WRITEDATA, &result);
|
||||
CURL_SETOPT(curl.get(), CURLOPT_NOSIGNAL, 1L);
|
||||
CURL_SETOPT(curl.get(), CURLOPT_TIMEOUT_MS,
|
||||
timeout / std::chrono::milliseconds(1));
|
||||
CURL_SETOPT(curl.get(), CURLOPT_TIMEOUT_MS, timeout / std::chrono::milliseconds(1));
|
||||
CURL_SETOPT(curl.get(), CURLOPT_SHARE, GetCurlShareHandle());
|
||||
// 10 min
|
||||
CURL_SETOPT(curl.get(), CURLOPT_DNS_CACHE_TIMEOUT,
|
||||
std::chrono::minutes(10) / std::chrono::seconds(1));
|
||||
CURL_SETOPT(curl.get(), CURLOPT_DNS_CACHE_TIMEOUT, std::chrono::minutes(10) / std::chrono::seconds(1));
|
||||
|
||||
auto rc = curl_easy_perform(curl.get());
|
||||
if (rc != CURLE_OK) {
|
||||
TILE_LOG_WARNING_EVERY_SECOND("Failed to call [{}]: [#{}] {}", uri, (int)rc,
|
||||
curl_easy_strerror(rc));
|
||||
TILE_LOG_WARNING_EVERY_SECOND("Failed to call [{}]: [#{}] {}", uri, (int) rc, curl_easy_strerror(rc));
|
||||
return std::make_unexpected(-rc);
|
||||
}
|
||||
|
||||
long resp_code;
|
||||
curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &resp_code);
|
||||
if (resp_code != 200) {
|
||||
return std::make_unexpected(resp_code);
|
||||
}
|
||||
if (resp_code != 200) { return std::make_unexpected(resp_code); }
|
||||
return result;
|
||||
}
|
||||
|
||||
std::expected<std::string, int> HttpGet(const std::string &uri,
|
||||
std::chrono::nanoseconds timeout) {
|
||||
if (s_http_get_mock_handler) {
|
||||
return s_http_get_mock_handler(uri, timeout);
|
||||
}
|
||||
std::expected<std::string, int>
|
||||
HttpGet(const std::string &uri, std::chrono::nanoseconds timeout)
|
||||
{
|
||||
if (s_http_get_mock_handler) { return s_http_get_mock_handler(uri, timeout); }
|
||||
|
||||
std::string result;
|
||||
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl{curl_easy_init(),
|
||||
&curl_easy_cleanup};
|
||||
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl{curl_easy_init(), &curl_easy_cleanup};
|
||||
CURL_SETOPT(curl.get(), CURLOPT_URL, uri.c_str());
|
||||
CURL_SETOPT(curl.get(), CURLOPT_WRITEFUNCTION, HttpWriteCallback);
|
||||
CURL_SETOPT(curl.get(), CURLOPT_WRITEDATA, &result);
|
||||
CURL_SETOPT(curl.get(), CURLOPT_NOSIGNAL, 1);
|
||||
CURL_SETOPT(curl.get(), CURLOPT_TIMEOUT_MS,
|
||||
timeout / std::chrono::milliseconds(1));
|
||||
CURL_SETOPT(curl.get(), CURLOPT_TIMEOUT_MS, timeout / std::chrono::milliseconds(1));
|
||||
auto rc = curl_easy_perform(curl.get());
|
||||
if (rc != CURLE_OK) {
|
||||
TILE_LOG_WARNING_EVERY_SECOND("Failed to call [{}]: [#{}] {}", uri, (int)rc,
|
||||
curl_easy_strerror(rc));
|
||||
TILE_LOG_WARNING_EVERY_SECOND("Failed to call [{}]: [#{}] {}", uri, (int) rc, curl_easy_strerror(rc));
|
||||
return std::make_unexpected(-rc);
|
||||
}
|
||||
long resp_code;// NOLINT: `CURLINFO_RESPONSE_CODE` needs a `long`..
|
||||
curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &resp_code);
|
||||
if (resp_code != 200) {
|
||||
return std::make_unexpected(resp_code);
|
||||
}
|
||||
if (resp_code != 200) { return std::make_unexpected(resp_code); }
|
||||
return result;
|
||||
}
|
||||
|
||||
void SetHttpPostMockHandler(HttpPostMockHandler &&http_post_mock_handler) {
|
||||
void
|
||||
SetHttpPostMockHandler(HttpPostMockHandler &&http_post_mock_handler)
|
||||
{
|
||||
s_http_post_mock_handler = std::move(http_post_mock_handler);
|
||||
}
|
||||
|
||||
void SetHttpGetMockHandler(HttpGetMockHandler &&http_get_mock_handler) {
|
||||
void
|
||||
SetHttpGetMockHandler(HttpGetMockHandler &&http_get_mock_handler)
|
||||
{
|
||||
s_http_get_mock_handler = std::move(http_get_mock_handler);
|
||||
}
|
||||
|
||||
|
@ -12,18 +12,21 @@
|
||||
namespace tile {
|
||||
|
||||
namespace internal {
|
||||
using HttpPostMockHandler = std::function<std::expected<std::string, int>(
|
||||
const std::string &, const std::vector<std::string> &, const std::string &,
|
||||
using HttpPostMockHandler =
|
||||
std::function<std::expected<std::string, int>(const std::string &,
|
||||
const std::vector<std::string> &,
|
||||
const std::string &,
|
||||
std::chrono::nanoseconds)>;
|
||||
|
||||
using HttpGetMockHandler = std::function<std::expected<std::string, int>(
|
||||
const std::string &, std::chrono::nanoseconds)>;
|
||||
using HttpGetMockHandler =
|
||||
std::function<std::expected<std::string, int>(const std::string &, std::chrono::nanoseconds)>;
|
||||
|
||||
std::expected<std::string, int>
|
||||
HttpPost(const std::string &uri, const std::vector<std::string> &headers,
|
||||
const std::string &body, std::chrono::nanoseconds timeout);
|
||||
std::expected<std::string, int> HttpGet(const std::string &uri,
|
||||
HttpPost(const std::string &uri,
|
||||
const std::vector<std::string> &headers,
|
||||
const std::string &body,
|
||||
std::chrono::nanoseconds timeout);
|
||||
std::expected<std::string, int> HttpGet(const std::string &uri, std::chrono::nanoseconds timeout);
|
||||
|
||||
void SetHttpPostMockHandler(HttpPostMockHandler &&handler);
|
||||
void SetHttpGetMockHandler(HttpGetMockHandler &&handler);
|
||||
|
@ -10,10 +10,13 @@ namespace tile {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
std::string HttpGetTestURI(int code) {
|
||||
std::string
|
||||
HttpGetTestURI(int code)
|
||||
{
|
||||
static const std::string kHttpCodeTestURI = "http://httpstat.us/";
|
||||
return kHttpCodeTestURI + std::to_string(code);
|
||||
}
|
||||
|
||||
constexpr auto kTimeout = std::chrono::seconds(5);
|
||||
}// namespace
|
||||
|
||||
@ -21,14 +24,13 @@ constexpr auto kTimeout = std::chrono::seconds(5);
|
||||
do { \
|
||||
auto test_anonymous_http_res = HttpGet(uri, kTimeout); \
|
||||
EXPECT_TRUE(code == 200 || !test_anonymous_http_res); \
|
||||
if (!test_anonymous_http_res) { \
|
||||
EXPECT_EQ(test_anonymous_http_res.error(), code); \
|
||||
} \
|
||||
if (!test_anonymous_http_res) { EXPECT_EQ(test_anonymous_http_res.error(), code); } \
|
||||
} while (0);
|
||||
|
||||
#define TEST_HTTP_GET(code) TEST_HTTP_GET_URI(HttpGetTestURI(code), code)
|
||||
|
||||
TEST(CURL, Get) {
|
||||
TEST(CURL, Get)
|
||||
{
|
||||
// TEST_HTTP_GET_URI("http://baidu.com/", 200);
|
||||
// 1xx
|
||||
TEST_HTTP_GET(100);// Continue
|
||||
|
@ -7,7 +7,8 @@
|
||||
namespace tile {
|
||||
namespace internal {
|
||||
namespace detail {
|
||||
template <typename T, typename Tag = void> struct EarlyInitInstance {
|
||||
template<typename T, typename Tag = void>
|
||||
struct EarlyInitInstance {
|
||||
static NeverDestroyed<T> object;
|
||||
};
|
||||
|
||||
@ -16,7 +17,10 @@ NeverDestroyed<T> EarlyInitInstance<T, Tag>::object;
|
||||
|
||||
}// namespace detail
|
||||
|
||||
template <typename T, typename Tag = void> const T &EarlyInitConstant() {
|
||||
template<typename T, typename Tag = void>
|
||||
const T &
|
||||
EarlyInitConstant()
|
||||
{
|
||||
return *detail::EarlyInitInstance<T, Tag>::object.Get();
|
||||
}
|
||||
|
||||
|
@ -10,33 +10,37 @@
|
||||
|
||||
namespace tile {
|
||||
namespace detail {
|
||||
template <typename S, typename T> struct is_streamable {
|
||||
template<typename S, typename T>
|
||||
struct is_streamable {
|
||||
template<typename SS, typename TT>
|
||||
static auto Test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(),
|
||||
std::true_type());
|
||||
template <typename...> static auto Test(...) -> std::false_type;
|
||||
static auto Test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());
|
||||
template<typename...>
|
||||
static auto Test(...) -> std::false_type;
|
||||
static constexpr bool value = decltype(Test<S, T>(0))::value;
|
||||
};
|
||||
|
||||
template <typename T> struct has_to_string {
|
||||
template<typename T>
|
||||
struct has_to_string {
|
||||
template<typename U>
|
||||
static auto Test(int) -> decltype(std::declval<U>().ToString(),
|
||||
std::true_type());
|
||||
template <typename...> static auto Test(...) -> std::false_type;
|
||||
static auto Test(int) -> decltype(std::declval<U>().ToString(), std::true_type());
|
||||
template<typename...>
|
||||
static auto Test(...) -> std::false_type;
|
||||
static constexpr bool value = decltype(Test<T>(0))::value;
|
||||
};
|
||||
|
||||
template <typename T> struct can_to_string {
|
||||
template<typename T>
|
||||
struct can_to_string {
|
||||
template<typename U>
|
||||
static auto Test(int) -> decltype(ToString(std::declval<U>()),
|
||||
std::true_type());
|
||||
template <typename...> static auto Test(...) -> std::false_type;
|
||||
static auto Test(int) -> decltype(ToString(std::declval<U>()), std::true_type());
|
||||
template<typename...>
|
||||
static auto Test(...) -> std::false_type;
|
||||
static constexpr bool value = decltype(Test<T>(0))::value;
|
||||
};
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
std::string format_as_impl(const std::chrono::duration<Rep, Period> &val,
|
||||
const char *suffix) {
|
||||
std::string
|
||||
format_as_impl(const std::chrono::duration<Rep, Period> &val, const char *suffix)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << val.count() << suffix;
|
||||
return ss.str();
|
||||
@ -46,46 +50,46 @@ std::string format_as_impl(const std::chrono::duration<Rep, Period> &val,
|
||||
}// namespace tile
|
||||
|
||||
template<typename T>
|
||||
auto format_as(const T &val)
|
||||
-> tile::enable_if_t<
|
||||
tile::detail::is_streamable<std::ostream, T>::value &&
|
||||
!std::is_same<std::string,
|
||||
tile::internal::remove_cvref_t<T>>::value,
|
||||
std::string> {
|
||||
auto
|
||||
format_as(const T &val)
|
||||
-> tile::enable_if_t<tile::detail::is_streamable<std::ostream, T>::value
|
||||
&& !std::is_same<std::string, tile::internal::remove_cvref_t<T>>::value,
|
||||
std::string>
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << val;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto format_as(const T &val)
|
||||
-> tile::enable_if_t<!tile::detail::is_streamable<std::ostream, T>::value &&
|
||||
tile::detail::has_to_string<T>::value &&
|
||||
!tile::detail::can_to_string<T>::value,
|
||||
std::string> {
|
||||
auto
|
||||
format_as(const T &val)
|
||||
-> tile::enable_if_t<!tile::detail::is_streamable<std::ostream, T>::value && tile::detail::has_to_string<T>::value
|
||||
&& !tile::detail::can_to_string<T>::value,
|
||||
std::string>
|
||||
{
|
||||
return val.ToString();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto format_as(const T &val)
|
||||
-> tile::enable_if_t<!tile::detail::is_streamable<std::ostream, T>::value &&
|
||||
!tile::detail::has_to_string<T>::value &&
|
||||
tile::detail::can_to_string<T>::value,
|
||||
std::string> {
|
||||
auto
|
||||
format_as(const T &val)
|
||||
-> tile::enable_if_t<!tile::detail::is_streamable<std::ostream, T>::value && !tile::detail::has_to_string<T>::value
|
||||
&& tile::detail::can_to_string<T>::value,
|
||||
std::string>
|
||||
{
|
||||
return ToString(val);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto format_as(T *ptr)
|
||||
-> tile::enable_if_t<!std::is_void<tile::remove_cvref_t<T>>::value,
|
||||
void *> {
|
||||
auto
|
||||
format_as(T *ptr) -> tile::enable_if_t<!std::is_void<tile::remove_cvref_t<T>>::value, void *>
|
||||
{
|
||||
return reinterpret_cast<void *>(ptr);
|
||||
}
|
||||
|
||||
#define TILE_INSTANCE_CHRONO_FORMAT_AS(type, suffix) \
|
||||
inline std::string format_as(const type &val) { \
|
||||
return tile::detail::format_as_impl(val, suffix); \
|
||||
}
|
||||
inline std::string format_as(const type &val) { return tile::detail::format_as_impl(val, suffix); }
|
||||
|
||||
TILE_INSTANCE_CHRONO_FORMAT_AS(std::chrono::nanoseconds, "ns")
|
||||
TILE_INSTANCE_CHRONO_FORMAT_AS(std::chrono::microseconds, "us")
|
||||
|
@ -6,11 +6,15 @@
|
||||
|
||||
#include <chrono>
|
||||
|
||||
template <typename T> static std::string ChronoFormat(const T &val) {
|
||||
template<typename T>
|
||||
static std::string
|
||||
ChronoFormat(const T &val)
|
||||
{
|
||||
return fmt::format("{}", val);
|
||||
}
|
||||
|
||||
TEST(Format, Chrono) {
|
||||
TEST(Format, Chrono)
|
||||
{
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::nanoseconds(1)), "1ns");
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::microseconds(1)), "1us");
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::milliseconds(1)), "1ms");
|
||||
@ -19,25 +23,25 @@ TEST(Format, Chrono) {
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::hours(1)), "1h");
|
||||
}
|
||||
|
||||
TEST(Format, ChronoRandom) {
|
||||
TEST(Format, ChronoRandom)
|
||||
{
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
int n = tile::Random(0, 1000000);
|
||||
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::nanoseconds(n)),
|
||||
fmt::format("{}ns", n));
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::microseconds(n)),
|
||||
fmt::format("{}us", n));
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::milliseconds(n)),
|
||||
fmt::format("{}ms", n));
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::nanoseconds(n)), fmt::format("{}ns", n));
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::microseconds(n)), fmt::format("{}us", n));
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::milliseconds(n)), fmt::format("{}ms", n));
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::seconds(n)), fmt::format("{}s", n));
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::minutes(n)), fmt::format("{}m", n));
|
||||
EXPECT_EQ(ChronoFormat(std::chrono::hours(n)), fmt::format("{}h", n));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Format, HasToString) {
|
||||
TEST(Format, HasToString)
|
||||
{
|
||||
struct Foo {
|
||||
std::string ToString() const { return "Foo"; }
|
||||
};
|
||||
|
||||
EXPECT_EQ(fmt::format("{}", Foo()), "Foo");
|
||||
}
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
namespace tile {
|
||||
namespace internal {
|
||||
std::size_t IndexAlloc::Next() {
|
||||
std::size_t
|
||||
IndexAlloc::Next()
|
||||
{
|
||||
ScopedLock _(lock_);
|
||||
if (!recycled_.empty()) {
|
||||
auto rc = recycled_.back();
|
||||
@ -14,7 +16,9 @@ std::size_t IndexAlloc::Next() {
|
||||
return current_++;
|
||||
}
|
||||
|
||||
void IndexAlloc::Free(std::size_t index) {
|
||||
void
|
||||
IndexAlloc::Free(std::size_t index)
|
||||
{
|
||||
ScopedLock _(lock_);
|
||||
recycled_.push_back(index);
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ namespace internal {
|
||||
|
||||
class IndexAlloc {
|
||||
public:
|
||||
template <typename Tag> static IndexAlloc *For() {
|
||||
template<typename Tag>
|
||||
static IndexAlloc *For()
|
||||
{
|
||||
static NeverDestroyedSingleton<IndexAlloc> ia;
|
||||
return ia.Get();
|
||||
}
|
||||
|
@ -6,12 +6,18 @@
|
||||
|
||||
namespace tile {
|
||||
namespace internal {
|
||||
template <typename T, typename Tag = void> T *LazyInit() {
|
||||
template<typename T, typename Tag = void>
|
||||
T *
|
||||
LazyInit()
|
||||
{
|
||||
static NeverDestroyed<T> t;
|
||||
return t.Get();
|
||||
};
|
||||
|
||||
template <typename T, typename Tag = void> const T &LazyInitConstant() {
|
||||
template<typename T, typename Tag = void>
|
||||
const T &
|
||||
LazyInitConstant()
|
||||
{
|
||||
static NeverDestroyed<T> t;
|
||||
return *t;
|
||||
}
|
||||
|
@ -6,10 +6,16 @@
|
||||
#include <mutex>
|
||||
|
||||
namespace tile {
|
||||
bool VLogIsOn(int n) { return true; }
|
||||
bool
|
||||
VLogIsOn(int n)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
class LogMessage::Impl {
|
||||
public:
|
||||
Impl(const char *file, int line, int severity) : lm_(file, line, severity) {}
|
||||
|
||||
std::ostream &stream() { return lm_.stream(); }
|
||||
|
||||
private:
|
||||
@ -19,21 +25,32 @@ private:
|
||||
class LogMessageFatal::Impl {
|
||||
public:
|
||||
Impl(const char *file, int line) : lm_(file, line) {}
|
||||
|
||||
std::ostream &stream() { return lm_.stream(); }
|
||||
|
||||
private:
|
||||
google::LogMessageFatal lm_;
|
||||
};
|
||||
|
||||
LogMessage::LogMessage(const char *file, int line, int severity)
|
||||
: impl_(new Impl(file, line, severity)) {}
|
||||
LogMessage::~LogMessage() = default;
|
||||
std::ostream &LogMessage::stream() { return impl_->stream(); }
|
||||
LogMessage::LogMessage(const char *file, int line, int severity) : impl_(new Impl(file, line, severity)) {}
|
||||
|
||||
LogMessage::~LogMessage() = default;
|
||||
|
||||
std::ostream &
|
||||
LogMessage::stream()
|
||||
{
|
||||
return impl_->stream();
|
||||
}
|
||||
|
||||
LogMessageFatal::LogMessageFatal(const char *file, int line) : impl_(new Impl(file, line)) {}
|
||||
|
||||
LogMessageFatal::LogMessageFatal(const char *file, int line)
|
||||
: impl_(new Impl(file, line)) {}
|
||||
LogMessageFatal::~LogMessageFatal() = default;
|
||||
std::ostream &LogMessageFatal::stream() { return impl_->stream(); }
|
||||
|
||||
std::ostream &
|
||||
LogMessageFatal::stream()
|
||||
{
|
||||
return impl_->stream();
|
||||
}
|
||||
|
||||
}// namespace tile
|
||||
|
||||
@ -41,43 +58,54 @@ namespace tile {
|
||||
namespace internal {
|
||||
namespace logging {
|
||||
namespace {
|
||||
std::vector<PrefixAppender *> *GetProviers() {
|
||||
std::vector<PrefixAppender *> *
|
||||
GetProviers()
|
||||
{
|
||||
static std::vector<PrefixAppender *> providers;
|
||||
return &providers;
|
||||
}
|
||||
}// namespace
|
||||
|
||||
namespace details {
|
||||
std::string DescribeFormatArguments(const std::vector<std::string> &args) {
|
||||
std::string
|
||||
DescribeFormatArguments(const std::vector<std::string> &args)
|
||||
{
|
||||
return fmt::format("{}", fmt::join(args.begin(), args.end(), ", "));
|
||||
}
|
||||
|
||||
}// namespace details
|
||||
void InstallPrefixProvider(PrefixAppender *writer) {
|
||||
|
||||
void
|
||||
InstallPrefixProvider(PrefixAppender *writer)
|
||||
{
|
||||
GetProviers()->push_back(writer);
|
||||
}
|
||||
void WritePrefixTo(std::string *to) {
|
||||
|
||||
void
|
||||
WritePrefixTo(std::string *to)
|
||||
{
|
||||
for (auto &&appender : *GetProviers()) {
|
||||
auto was = to->size();
|
||||
appender(to);
|
||||
if (to->size() != was) {
|
||||
to->push_back(' ');
|
||||
}
|
||||
if (to->size() != was) { to->push_back(' '); }
|
||||
}
|
||||
}
|
||||
}// namespace logging
|
||||
}// namespace internal
|
||||
}// namespace tile
|
||||
|
||||
namespace tile {
|
||||
|
||||
LogMessageTime::LogMessageTime()
|
||||
: time_struct_(), timestamp_(0), usecs_(0), gmtoffset_(0) {}
|
||||
LogMessageTime::LogMessageTime() : time_struct_(), timestamp_(0), usecs_(0), gmtoffset_(0) {}
|
||||
|
||||
LogMessageTime::LogMessageTime(std::tm t) {
|
||||
LogMessageTime::LogMessageTime(std::tm t)
|
||||
{
|
||||
std::time_t timestamp = std::mktime(&t);
|
||||
init(t, timestamp, 0);
|
||||
}
|
||||
|
||||
LogMessageTime::LogMessageTime(std::time_t timestamp, WallTime now) {
|
||||
LogMessageTime::LogMessageTime(std::time_t timestamp, WallTime now)
|
||||
{
|
||||
std::tm t;
|
||||
if (FLAGS_log_utc_time)
|
||||
gmtime_r(×tamp, &t);
|
||||
@ -86,8 +114,9 @@ LogMessageTime::LogMessageTime(std::time_t timestamp, WallTime now) {
|
||||
init(t, timestamp, now);
|
||||
}
|
||||
|
||||
void LogMessageTime::init(const std::tm &t, std::time_t timestamp,
|
||||
WallTime now) {
|
||||
void
|
||||
LogMessageTime::init(const std::tm &t, std::time_t timestamp, WallTime now)
|
||||
{
|
||||
time_struct_ = t;
|
||||
timestamp_ = timestamp;
|
||||
usecs_ = static_cast<std::int32_t>((now - timestamp) * 1000000);
|
||||
@ -95,7 +124,9 @@ void LogMessageTime::init(const std::tm &t, std::time_t timestamp,
|
||||
CalcGmtOffset();
|
||||
}
|
||||
|
||||
void LogMessageTime::CalcGmtOffset() {
|
||||
void
|
||||
LogMessageTime::CalcGmtOffset()
|
||||
{
|
||||
std::tm gmt_struct;
|
||||
int isDst = 0;
|
||||
if (FLAGS_log_utc_time) {
|
||||
@ -111,36 +142,54 @@ void LogMessageTime::CalcGmtOffset() {
|
||||
const long hour_secs = 3600;
|
||||
// If the Daylight Saving Time(isDst) is active subtract an hour from the
|
||||
// current timestamp.
|
||||
gmtoffset_ =
|
||||
static_cast<long int>(timestamp_ - gmt_sec + (isDst ? hour_secs : 0));
|
||||
gmtoffset_ = static_cast<long int>(timestamp_ - gmt_sec + (isDst ? hour_secs : 0));
|
||||
}
|
||||
|
||||
LogSink::~LogSink() {}
|
||||
void LogSink::send(LogSeverity severity, const char *full_filename,
|
||||
const char *base_filename, int line,
|
||||
const LogMessageTime &logmsgtime, const char *message,
|
||||
size_t message_len) {
|
||||
|
||||
void
|
||||
LogSink::send(LogSeverity severity,
|
||||
const char *full_filename,
|
||||
const char *base_filename,
|
||||
int line,
|
||||
const LogMessageTime &logmsgtime,
|
||||
const char *message,
|
||||
size_t message_len)
|
||||
{
|
||||
|
||||
// do nothing
|
||||
}
|
||||
void LogSink::WaitTillSent() {}
|
||||
std::string LogSink::ToString(LogSeverity severity, const char *file, int line,
|
||||
|
||||
void
|
||||
LogSink::WaitTillSent()
|
||||
{}
|
||||
|
||||
std::string
|
||||
LogSink::ToString(LogSeverity severity,
|
||||
const char *file,
|
||||
int line,
|
||||
const LogMessageTime &logmsgtime,
|
||||
const char *message, size_t message_len) {
|
||||
return google::LogSink::ToString(severity, file, line, logmsgtime.tm(),
|
||||
message, message_len);
|
||||
const char *message,
|
||||
size_t message_len)
|
||||
{
|
||||
return google::LogSink::ToString(severity, file, line, logmsgtime.tm(), message, message_len);
|
||||
}
|
||||
|
||||
class LogSinkWrapper : public google::LogSink {
|
||||
public:
|
||||
LogSinkWrapper(tile::LogSink *dest) : dest_(dest) {}
|
||||
|
||||
~LogSinkWrapper() override {}
|
||||
void send(LogSeverity severity, const char *full_filename,
|
||||
const char *base_filename, int line,
|
||||
const google::LogMessageTime &logmsgtime, const char *message,
|
||||
size_t message_len) override {
|
||||
dest_->send(severity, full_filename, base_filename, line, logmsgtime.tm(),
|
||||
message, message_len);
|
||||
|
||||
void send(LogSeverity severity,
|
||||
const char *full_filename,
|
||||
const char *base_filename,
|
||||
int line,
|
||||
const google::LogMessageTime &logmsgtime,
|
||||
const char *message,
|
||||
size_t message_len) override
|
||||
{
|
||||
dest_->send(severity, full_filename, base_filename, line, logmsgtime.tm(), message, message_len);
|
||||
}
|
||||
|
||||
void WaitTillSent() override { dest_->WaitTillSent(); }
|
||||
@ -157,18 +206,20 @@ struct LogSinkPair {
|
||||
static std::map<LogSink *, LogSinkWrapper *> sink_registry;
|
||||
static std::mutex sink_registry_mutex;
|
||||
|
||||
void AddLogSink(LogSink *dest) {
|
||||
void
|
||||
AddLogSink(LogSink *dest)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sink_registry_mutex);
|
||||
if (sink_registry.find(dest) != sink_registry.end()) {
|
||||
return;
|
||||
}
|
||||
if (sink_registry.find(dest) != sink_registry.end()) { return; }
|
||||
|
||||
auto wrapper = new LogSinkWrapper(dest);
|
||||
sink_registry[dest] = wrapper;
|
||||
google::AddLogSink(wrapper);
|
||||
}
|
||||
|
||||
void RemoveLogSink(LogSink *dest) {
|
||||
void
|
||||
RemoveLogSink(LogSink *dest)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sink_registry_mutex);
|
||||
auto iter = sink_registry.find(dest);
|
||||
if (iter != sink_registry.end()) {
|
||||
@ -177,11 +228,22 @@ void RemoveLogSink(LogSink *dest) {
|
||||
delete iter->second;
|
||||
}
|
||||
}
|
||||
void SetStderrLogging(LogSeverity min_severity) {
|
||||
|
||||
void
|
||||
SetStderrLogging(LogSeverity min_severity)
|
||||
{
|
||||
google::SetStderrLogging(min_severity);
|
||||
}
|
||||
void LogToStderr() { google::LogToStderr(); }
|
||||
const char *GetLogSeverityName(LogSeverity severity) {
|
||||
|
||||
void
|
||||
LogToStderr()
|
||||
{
|
||||
google::LogToStderr();
|
||||
}
|
||||
|
||||
const char *
|
||||
GetLogSeverityName(LogSeverity severity)
|
||||
{
|
||||
return google::GetLogSeverityName(severity);
|
||||
}
|
||||
}// namespace tile
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user