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

This commit is contained in:
tqcq 2024-08-23 21:40:44 +08:00
parent 64200d42cc
commit 481015e1c6
268 changed files with 23084 additions and 22201 deletions

View File

@ -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

View File

@ -15,19 +15,19 @@ constexpr std::size_t max_align_v = alignof(max_align_t);
// @sa: https://github.com/facebook/folly/blob/master/folly/lang/Align.h
//
// Update at 20201124: Well, AMD's Zen 3 does the same.
constexpr std::size_t hardware_destructive_interference_size = 128;
constexpr std::size_t hardware_destructive_interference_size = 128;
constexpr std::size_t hardware_constructive_interference_size = 64;
#elif defined(__powerpc64__)
// These values are read from
// `/sys/devices/system/cpu/cpu0/cache/index*/coherency_line_size`
constexpr std::size_t hardware_destructive_interference_size = 128;
constexpr std::size_t hardware_destructive_interference_size = 128;
constexpr std::size_t hardware_constructive_interference_size = 128;
#elif defined(__ARM_ARCH_5T__)
constexpr std::size_t hardware_destructive_interference_size = 32;
constexpr std::size_t hardware_destructive_interference_size = 32;
constexpr std::size_t hardware_constructive_interference_size = 32;
#elif defined(__arm__)
@ -36,7 +36,7 @@ constexpr std::size_t hardware_constructive_interference_size = 32;
// are even implementations with cache line sizes configurable at boot
// time.
constexpr std::size_t hardware_destructive_interference_size = 64;
constexpr std::size_t hardware_destructive_interference_size = 64;
constexpr std::size_t hardware_constructive_interference_size = 64;
#elif defined(__aarch64__)
@ -47,13 +47,13 @@ constexpr std::size_t hardware_constructive_interference_size = 64;
// Let's ignore those CPUs for now.
//
// @sa: https://www.mono-project.com/news/2016/09/12/arm64-icache/
constexpr std::size_t hardware_destructive_interference_size = 64;
constexpr std::size_t hardware_destructive_interference_size = 64;
constexpr std::size_t hardware_constructive_interference_size = 64;
#else
constexpr std::size_t hardware_constructive_interference_size = max_align_v;
constexpr std::size_t hardware_destructive_interference_size = max_align_v;
constexpr std::size_t hardware_destructive_interference_size = max_align_v;
// #error Unsupported architecture.
// #pragma message("Unsupported architecture, use max_align_v")
@ -62,6 +62,6 @@ static_assert(hardware_constructive_interference_size >= max_align_v,
"hardware constructive interference size is too small");
static_assert(hardware_destructive_interference_size >= max_align_v,
"hardware destructive interference size is too small");
} // namespace tile
}// namespace tile
#endif // _TILE_BASE_ALIGN_H
#endif// _TILE_BASE_ALIGN_H

View File

@ -9,205 +9,214 @@
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());
}
explicit OwningBufferBlock(T storage) : storage_(std::move(storage)) {}
std::size_t size() const noexcept override { return storage_.size(); }
const char *data() const noexcept override { return reinterpret_cast<const char *>(storage_.data()); }
std::size_t size() const noexcept override { return storage_.size(); }
private:
T storage_;
T storage_;
};
} // namespace
}// namespace
namespace detail {
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) {
auto len = std::min(size - copied, iter->size());
memcpy(reinterpret_cast<char *>(buffer) + copied, iter->data(), len);
copied += len;
}
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) {
auto len = std::min(size - copied, iter->size());
memcpy(reinterpret_cast<char *>(buffer) + copied, iter->data(), len);
copied += len;
}
}
} // namespace detail
}// namespace detail
NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other)
: byte_size_(other.byte_size_) {
for (auto &&e : other.buffers_) {
NoncontiguousBuffer::NoncontiguousBuffer(const NoncontiguousBuffer &other) : byte_size_(other.byte_size_)
{
for (auto &&e : other.buffers_) {
auto b = object_pool::Get<NoncontiguousBuffer::buffer_t>().Leak();
*b = e;
buffers_.push_back(std::move(b));
}
auto b = object_pool::Get<NoncontiguousBuffer::buffer_t>().Leak();
*b = e;
buffers_.push_back(std::move(b));
}
}
NoncontiguousBuffer &
NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other) {
if (TILE_UNLIKELY(&other == this)) {
NoncontiguousBuffer::operator=(const NoncontiguousBuffer &other)
{
if (TILE_UNLIKELY(&other == this)) { return *this; }
Clear();
byte_size_ = other.byte_size_;
for (auto &&e : other.buffers_) {
auto b = object_pool::Get<NoncontiguousBuffer::buffer_t>().Leak();
*b = e;
buffers_.push_back(std::move(b));
}
return *this;
}
Clear();
byte_size_ = other.byte_size_;
for (auto &&e : other.buffers_) {
auto b = object_pool::Get<NoncontiguousBuffer::buffer_t>().Leak();
*b = e;
buffers_.push_back(std::move(b));
}
return *this;
}
void NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept {
TILE_CHECK_LE(bytes, byte_size_, "Skip bytes exceed buffer size");
byte_size_ -= bytes;
void
NoncontiguousBuffer::SkipSlow(std::size_t bytes) noexcept
{
TILE_CHECK_LE(bytes, byte_size_, "Skip bytes exceed buffer size");
byte_size_ -= bytes;
while (bytes) {
auto &&first = buffers_.front();
auto min_size = std::min(bytes, first.size());
if (min_size == first.size()) {
object_pool::Put<buffer_t>(buffers_.pop_front());
} else {
TILE_DCHECK_LT(min_size, first.size());
first.Skip(min_size);
while (bytes) {
auto &&first = buffers_.front();
auto min_size = std::min(bytes, first.size());
if (min_size == first.size()) {
object_pool::Put<buffer_t>(buffers_.pop_front());
} else {
TILE_DCHECK_LT(min_size, first.size());
first.Skip(min_size);
}
bytes -= min_size;
}
bytes -= min_size;
}
}
void NoncontiguousBuffer::ClearSlow() noexcept {
byte_size_ = 0;
while (!buffers_.empty()) {
object_pool::Put<buffer_t>(buffers_.pop_front());
}
void
NoncontiguousBuffer::ClearSlow() noexcept
{
byte_size_ = 0;
while (!buffers_.empty()) { object_pool::Put<buffer_t>(buffers_.pop_front()); }
}
void NoncontiguousBufferBuilder::InitializeNextBlock() {
if (current_) {
TILE_CHECK(SizeAvailable(),
"You should flush current block by `FlushCurrentBlock()`");
return;
}
current_ = MakeNativeBufferBlock();
used_ = 0;
}
void NoncontiguousBufferBuilder::FlushCurrentBlock() {
if (!used_) {
// current block is empty
return;
}
nb_.Append(PolymorphicBuffer(std::move(current_), 0, used_));
used_ = 0;
current_ = nullptr;
}
void NoncontiguousBufferBuilder::AppendSlow(const void *ptr,
std::size_t length) {
while (length) {
auto copying = std::min(length, SizeAvailable());
memcpy(data(), ptr, copying);
MarkWritten(copying);
ptr = static_cast<const char *>(ptr) + copying;
length -= copying;
}
}
void NoncontiguousBufferBuilder::AppendCopy(const NoncontiguousBuffer &buffer) {
for (auto &&e : buffer) {
Append(e.data(), e.size());
}
}
NoncontiguousBuffer CreateBufferSlow(Slice s) {
NoncontiguousBufferBuilder nbb;
nbb.Append(s);
return nbb.DestructiveGet();
}
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) {
max_bytes = std::min(max_bytes, nb.ByteSize());
std::string rc;
std::size_t left = max_bytes;
rc.reserve(max_bytes);
for (auto iter = nb.begin(); iter != nb.end() && left; ++iter) {
auto len = std::min(left, iter->size());
rc.append(iter->data(), len);
left -= len;
}
return rc;
}
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);
void
NoncontiguousBufferBuilder::InitializeNextBlock()
{
if (current_) {
TILE_CHECK(SizeAvailable(), "You should flush current block by `FlushCurrentBlock()`");
return;
}
}
std::string rc;
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
current_ = MakeNativeBufferBlock();
used_ = 0;
}
void
NoncontiguousBufferBuilder::FlushCurrentBlock()
{
if (!used_) {
// current block is empty
return;
}
nb_.Append(PolymorphicBuffer(std::move(current_), 0, used_));
used_ = 0;
current_ = nullptr;
}
void
NoncontiguousBufferBuilder::AppendSlow(const void *ptr, std::size_t length)
{
while (length) {
auto copying = std::min(length, SizeAvailable());
memcpy(data(), ptr, copying);
MarkWritten(copying);
ptr = static_cast<const char *>(ptr) + copying;
length -= copying;
}
}
void
NoncontiguousBufferBuilder::AppendCopy(const NoncontiguousBuffer &buffer)
{
for (auto &&e : buffer) { Append(e.data(), e.size()); }
}
NoncontiguousBuffer
CreateBufferSlow(Slice s)
{
NoncontiguousBufferBuilder nbb;
nbb.Append(s);
return nbb.DestructiveGet();
}
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)
{
max_bytes = std::min(max_bytes, nb.ByteSize());
std::string rc;
std::size_t left = max_bytes;
rc.reserve(max_bytes);
for (auto iter = nb.begin(); iter != nb.end() && left; ++iter) {
auto len = std::min(left, iter->size());
rc.append(iter->data(), len);
left -= len;
}
return rc;
}
std::string
FlattenSlowUntil(const NoncontiguousBuffer &nb, Slice delim, std::size_t max_bytes)
{
if (nb.Empty()) { return {}; }
{
// Avoiding the use of find_last_of
auto pos = rc.find(delim, old_len - std::min(old_len, delim.size()));
if (pos != Slice::npos) {
rc.erase(rc.begin() + pos + delim.size(), rc.end());
break;
}
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);
}
}
}
if (rc.size() > max_bytes) {
rc.erase(rc.begin() + max_bytes, rc.end());
}
return rc;
std::string rc;
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
{
// Avoiding the use of find_last_of
auto pos = rc.find(delim, old_len - std::min(old_len, delim.size()));
if (pos != Slice::npos) {
rc.erase(rc.begin() + pos + delim.size(), rc.end());
break;
}
}
}
if (rc.size() > max_bytes) { rc.erase(rc.begin() + max_bytes, rc.end()); }
return rc;
}
PolymorphicBuffer MakeReferencingBuffer(const void *ptr, std::size_t size) {
return MakeReferencingBuffer(ptr, size, [] {});
PolymorphicBuffer
MakeReferencingBuffer(const void *ptr, std::size_t size)
{
return MakeReferencingBuffer(ptr, size, [] {});
}
PolymorphicBuffer MakeForeignBuffer(std::string buffer) {
auto size = buffer.size();
return PolymorphicBuffer(
MakeRefCounted<OwningBufferBlock<std::string>>(std::move(buffer)), 0,
size);
PolymorphicBuffer
MakeForeignBuffer(std::string buffer)
{
auto size = buffer.size();
return PolymorphicBuffer(MakeRefCounted<OwningBufferBlock<std::string>>(std::move(buffer)), 0, size);
}
template <typename T>
PolymorphicBuffer MakeForeignBuffer(std::vector<T> buffer) {
auto size = buffer.size() * sizeof(T);
return PolymorphicBuffer(
MakeRefCounted<OwningBufferBlock<std::vector<T>>>(std::move(buffer)), 0,
size);
template<typename T>
PolymorphicBuffer
MakeForeignBuffer(std::vector<T> buffer)
{
auto size = buffer.size() * sizeof(T);
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,51 +233,50 @@ 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;
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; }
buffer.Skip(min_len);
prefix.RemovePrefix(min_len);
}
buffer.Skip(min_len);
prefix.RemovePrefix(min_len);
}
return prefix.empty();
return prefix.empty();
}
bool EndsWith(NoncontiguousBuffer buffer, Slice suffix) {
TILE_NOT_IMPLEMENTED("");
return false;
}
bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix) {
if (buffer.ByteSize() < prefix.size()) {
bool
EndsWith(NoncontiguousBuffer buffer, Slice suffix)
{
TILE_NOT_IMPLEMENTED("");
return false;
}
}
while (!buffer.Empty() && prefix.empty()) {
auto first = buffer.FirstContiguous();
auto min_len = std::min(first.size(), prefix.size());
bool
StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix)
{
if (buffer.ByteSize() < prefix.size()) { return false; }
if (!EqualsIgnoreCase(first.substr(0, min_len),
prefix.substr(0, min_len))) {
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; }
buffer.Skip(min_len);
prefix.RemovePrefix(min_len);
}
buffer.Skip(min_len);
prefix.RemovePrefix(min_len);
}
return prefix.empty();
return prefix.empty();
}
bool EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix) {
TILE_NOT_IMPLEMENTED("");
bool
EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix)
{
TILE_NOT_IMPLEMENTED("");
}
} // namespace tile
}// namespace tile

View File

@ -19,355 +19,376 @@
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 *> {
return c.data();
template<typename T>
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) {
return c;
template<std::size_t N>
constexpr auto
data(const char (&c)[N]) -> decltype(c)
{
return c;
}
template <typename T> constexpr std::size_t size(const T &c) {
return c.size();
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 <> struct size_impl<0> {
static constexpr std::size_t size(const char (&c)[0]) { return 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 <std::size_t N> constexpr std::size_t size(const char (&c)[N]) {
return size_impl<N>::size(c);
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])
{
return size_impl<N>::size(c);
}
template <typename A1> constexpr std::size_t total_size(const A1 &a1) {
return size(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) {
return size(a1) + total_size(args...);
template<typename A1, typename... Args>
constexpr std::size_t
total_size(const A1 &a1, const Args &...args)
{
return size(a1) + total_size(args...);
}
} // namespace detail
}// namespace detail
class NoncontiguousBuffer {
// <buffer_t, buffer_t::chain>
using LinkedBuffers =
internal::SinglyLinkedList<PolymorphicBuffer, &PolymorphicBuffer::chain>;
// <buffer_t, buffer_t::chain>
using LinkedBuffers = internal::SinglyLinkedList<PolymorphicBuffer, &PolymorphicBuffer::chain>;
public:
using iterator = LinkedBuffers::iterator;
using const_iterator = LinkedBuffers::const_iterator;
using buffer_t = PolymorphicBuffer;
using iterator = LinkedBuffers::iterator;
using const_iterator = LinkedBuffers::const_iterator;
using buffer_t = PolymorphicBuffer;
constexpr NoncontiguousBuffer() = default;
constexpr NoncontiguousBuffer() = default;
// 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;
// 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; }
Clear();
std::swap(byte_size_, other.byte_size_);
buffers_ = std::move(other.buffers_);
return *this;
}
Clear();
std::swap(byte_size_, other.byte_size_);
buffers_ = std::move(other.buffers_);
return *this;
}
~NoncontiguousBuffer() { Clear(); }
~NoncontiguousBuffer() { Clear(); }
Slice FirstContiguous() const noexcept {
TILE_DCHECK(!Empty());
auto &&first = buffers_.front();
return Slice(first.data(), first.size());
}
void Skip(size_t bytes) noexcept {
TILE_DCHECK_LE(bytes, ByteSize());
if (TILE_UNLIKELY(bytes == 0)) {
} else if (bytes < buffers_.front().size()) {
buffers_.front().Skip(bytes);
byte_size_ -= bytes;
} else {
SkipSlow(bytes);
}
}
NoncontiguousBuffer Cut(std::size_t bytes) {
TILE_DCHECK_LE(bytes, ByteSize());
TILE_DCHECK_GE(bytes, 0);
NoncontiguousBuffer rc;
auto left = bytes;
while (left && left >= buffers_.front().size()) {
left -= buffers_.front().size();
rc.buffers_.push_back(buffers_.pop_front());
Slice FirstContiguous() const noexcept
{
TILE_DCHECK(!Empty());
auto &&first = buffers_.front();
return Slice(first.data(), first.size());
}
if (TILE_LIKELY(left > 0)) {
auto ncb = object_pool::Get<PolymorphicBuffer>().Leak();
*ncb = buffers_.front();
ncb->set_size(left);
rc.buffers_.push_back(ncb);
buffers_.front().Skip(left);
}
rc.byte_size_ = bytes;
byte_size_ -= bytes;
return rc;
}
void Append(PolymorphicBuffer buffer) {
if (TILE_UNLIKELY(buffer.size() == 0)) {
return;
void Skip(size_t bytes) noexcept
{
TILE_DCHECK_LE(bytes, ByteSize());
if (TILE_UNLIKELY(bytes == 0)) {
} else if (bytes < buffers_.front().size()) {
buffers_.front().Skip(bytes);
byte_size_ -= bytes;
} else {
SkipSlow(bytes);
}
}
auto block = object_pool::Get<PolymorphicBuffer>();
*block = std::move(buffer);
byte_size_ += block->size();
buffers_.push_back(block.Leak());
}
NoncontiguousBuffer Cut(std::size_t bytes)
{
TILE_DCHECK_LE(bytes, ByteSize());
TILE_DCHECK_GE(bytes, 0);
void Append(NoncontiguousBuffer buffer) {
byte_size_ += internal::Exchange(buffer.byte_size_, 0);
buffers_.splice(std::move(buffer.buffers_));
}
NoncontiguousBuffer rc;
auto left = bytes;
while (left && left >= buffers_.front().size()) {
left -= buffers_.front().size();
rc.buffers_.push_back(buffers_.pop_front());
}
std::size_t ByteSize() const noexcept { return byte_size_; }
bool Empty() const noexcept {
TILE_CHECK_EQ(buffers_.empty(), !byte_size_);
return !byte_size_;
}
void Clear() noexcept {
if (!Empty()) {
ClearSlow();
if (TILE_LIKELY(left > 0)) {
auto ncb = object_pool::Get<PolymorphicBuffer>().Leak();
*ncb = buffers_.front();
ncb->set_size(left);
rc.buffers_.push_back(ncb);
buffers_.front().Skip(left);
}
rc.byte_size_ = bytes;
byte_size_ -= bytes;
return rc;
}
}
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(); }
void Append(PolymorphicBuffer buffer)
{
if (TILE_UNLIKELY(buffer.size() == 0)) { return; }
auto block = object_pool::Get<PolymorphicBuffer>();
*block = std::move(buffer);
byte_size_ += block->size();
buffers_.push_back(block.Leak());
}
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
{
TILE_CHECK_EQ(buffers_.empty(), !byte_size_);
return !byte_size_;
}
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:
void SkipSlow(size_t bytes) noexcept;
void ClearSlow() noexcept;
void SkipSlow(size_t bytes) noexcept;
void ClearSlow() noexcept;
private:
std::size_t byte_size_{};
LinkedBuffers buffers_;
std::size_t byte_size_{};
LinkedBuffers buffers_;
};
class NoncontiguousBufferBuilder {
static constexpr auto kAppendViaCopyThreshold = 128;
static constexpr auto kAppendViaCopyThreshold = 128;
public:
NoncontiguousBufferBuilder() { InitializeNextBlock(); }
NoncontiguousBufferBuilder() { InitializeNextBlock(); }
// current write ptr
// It's size is available at `SizeAvailable()`
char *data() const noexcept { return current_->mutable_data() + used_; }
// current write ptr
// 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) {
TILE_DCHECK_LE(bytes, SizeAvailable(), "You're overflowing the buffer.");
used_ += bytes;
// Is fulled ?
if (TILE_UNLIKELY(!SizeAvailable())) {
FlushCurrentBlock();
InitializeNextBlock();
}
}
// Allocate new block
// return write ptr
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);
if (SizeAvailable() < bytes) {
FlushCurrentBlock();
InitializeNextBlock();
void MarkWritten(std::size_t bytes)
{
TILE_DCHECK_LE(bytes, SizeAvailable(), "You're overflowing the buffer.");
used_ += bytes;
// Is fulled ?
if (TILE_UNLIKELY(!SizeAvailable())) {
FlushCurrentBlock();
InitializeNextBlock();
}
}
auto ptr = data();
MarkWritten(bytes);
return ptr;
}
// Allocate new block
// return write ptr
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);
if (SizeAvailable() < bytes) {
FlushCurrentBlock();
InitializeNextBlock();
}
// Total number of bytes written
std::size_t ByteSize() const noexcept { return nb_.ByteSize() + used_; }
NoncontiguousBuffer DestructiveGet() noexcept {
FlushCurrentBlock();
return std::move(nb_);
}
void Append(const void *ptr, std::size_t length) {
auto current = data();
used_ += length;
if (TILE_LIKELY(used_ < current_->size())) {
memcpy(static_cast<void *>(current), ptr, length);
return;
}
used_ -= length;
AppendSlow(ptr, length);
}
void Append(Slice s) { return Append(s.data(), s.size()); }
void Append(const std::string &s) { Append(s.data(), s.size()); }
void Append(PolymorphicBuffer buffer) {
if (buffer.size() < kAppendViaCopyThreshold &&
SizeAvailable() >= buffer.size()) {
Append(buffer.data(), buffer.size());
return;
auto ptr = data();
MarkWritten(bytes);
return ptr;
}
if (used_) {
FlushCurrentBlock();
InitializeNextBlock();
// Total number of bytes written
std::size_t ByteSize() const noexcept { return nb_.ByteSize() + used_; }
NoncontiguousBuffer DestructiveGet() noexcept
{
FlushCurrentBlock();
return std::move(nb_);
}
nb_.Append(std::move(buffer));
}
void Append(NoncontiguousBuffer buffer) {
if (buffer.ByteSize() < kAppendViaCopyThreshold && SizeAvailable()) {
AppendCopy(buffer);
return;
void Append(const void *ptr, std::size_t length)
{
auto current = data();
used_ += length;
if (TILE_LIKELY(used_ < current_->size())) {
memcpy(static_cast<void *>(current), ptr, length);
return;
}
used_ -= length;
AppendSlow(ptr, length);
}
if (used_) {
FlushCurrentBlock();
InitializeNextBlock();
void Append(Slice s) { return Append(s.data(), s.size()); }
void Append(const std::string &s) { Append(s.data(), s.size()); }
void Append(PolymorphicBuffer buffer)
{
if (buffer.size() < kAppendViaCopyThreshold && SizeAvailable() >= buffer.size()) {
Append(buffer.data(), buffer.size());
return;
}
if (used_) {
FlushCurrentBlock();
InitializeNextBlock();
}
nb_.Append(std::move(buffer));
}
nb_.Append(std::move(buffer));
}
void Append(char c) { Append(static_cast<unsigned char>(c)); }
void Append(NoncontiguousBuffer buffer)
{
if (buffer.ByteSize() < kAppendViaCopyThreshold && SizeAvailable()) {
AppendCopy(buffer);
return;
}
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) {
auto current = data();
auto total = detail::total_size(buffers...);
used_ += total;
if (TILE_LIKELY(used_ < current_->size())) {
UncheckedAppend(current, buffers...);
return;
if (used_) {
FlushCurrentBlock();
InitializeNextBlock();
}
nb_.Append(std::move(buffer));
}
used_ -= total;
int dummy[] = {(Append(buffers), 0)...};
}
template <typename T>
auto Append(T) -> enable_if_t<std::is_same<T, int>::value> {
static_assert(sizeof(T) == 0, "Please use Append(char) instead.");
}
void Append(char c) { Append(static_cast<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)
{
auto current = data();
auto total = detail::total_size(buffers...);
used_ += total;
if (TILE_LIKELY(used_ < current_->size())) {
UncheckedAppend(current, buffers...);
return;
}
used_ -= total;
int dummy[] = {(Append(buffers), 0)...};
}
template<typename T>
auto Append(T) -> enable_if_t<std::is_same<T, int>::value>
{
static_assert(sizeof(T) == 0, "Please use Append(char) instead.");
}
private:
// Allocate a new buffer
void InitializeNextBlock();
// Allocate a new buffer
void InitializeNextBlock();
void FlushCurrentBlock();
void FlushCurrentBlock();
void AppendSlow(const void *ptr, std::size_t length);
void AppendCopy(const NoncontiguousBuffer &buffer);
void AppendSlow(const void *ptr, std::size_t length);
void AppendCopy(const NoncontiguousBuffer &buffer);
template <typename T, typename...>
inline void UncheckedAppend(char *ptr, const T &slice) {
memcpy(ptr, detail::data(slice), detail::size(slice));
}
template<typename T, typename...>
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) {
memcpy(ptr, detail::data(slice), detail::size(slice));
UncheckedAppend(ptr + detail::size(slice), slices...);
}
template<typename T, typename... Ts>
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...);
}
private:
NoncontiguousBuffer nb_;
std::size_t used_;
RefPtr<NativeBufferBlock> current_;
NoncontiguousBuffer nb_;
std::size_t used_;
RefPtr<NativeBufferBlock> current_;
};
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::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) {
if (TILE_LIKELY(max_bytes <= nb.FirstContiguous().size())) {
std::memcpy(buffer, nb.FirstContiguous().data(), max_bytes);
}
return detail::FlattenToSlowSlow(nb, buffer, max_bytes);
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());
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);
}
return detail::FlattenToSlowSlow(nb, buffer, max_bytes);
}
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>;
return PolymorphicBuffer(
MakeRefCounted<BufferBlock>(
ptr, size,
std::forward<typename std::remove_reference<F>::type>(completion_cb)),
0, 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>;
return PolymorphicBuffer(
MakeRefCounted<BufferBlock>(ptr, size, std::forward<typename std::remove_reference<F>::type>(completion_cb)), 0,
size);
}
PolymorphicBuffer MakeForeignBuffer(std::string buffer);
// `T` in (`std::byte`, integral types, floating types)
template <typename T>
template<typename T>
PolymorphicBuffer MakeForeignBuffer(std::vector<T> buffer);
bool StartsWith(NoncontiguousBuffer buffer, Slice prefix);
bool EndsWith(NoncontiguousBuffer buffer, Slice suffix);
bool StartsWithIgnoreCase(NoncontiguousBuffer buffer, Slice prefix);
bool EndsWithIgnoreCase(NoncontiguousBuffer buffer, Slice suffix);
} // namespace tile
}// namespace tile
#endif // TILE_BASE_BUFFER_H
#endif// TILE_BASE_BUFFER_H

View File

@ -19,104 +19,108 @@ namespace tile {
namespace {
template <std::size_t kSize>
struct alignas(hardware_destructive_interference_size) FixedNativeBufferBlock
: NativeBufferBlock {
template<std::size_t kSize>
struct alignas(hardware_destructive_interference_size) FixedNativeBufferBlock : NativeBufferBlock {
public:
static RefPtr<FixedNativeBufferBlock> New() {
// WARNING: don't use adopt_ptr
return RefPtr<FixedNativeBufferBlock<kSize>>(
adopt_ptr, object_pool::Get<FixedNativeBufferBlock<kSize>>().Leak());
}
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) {
ptr->Save();
object_pool::Put<FixedNativeBufferBlock<kSize>>(ptr);
}
// HACK: add ref ensure `RefCount<Base>`.
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); }
char *mutable_data() noexcept override { return buffer.data(); }
static constexpr std::size_t kBufferSize = kSize - sizeof(NativeBufferBlock);
std::array<char, kBufferSize> buffer;
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);
std::array<char, kBufferSize> buffer;
};
template <std::size_t kSize>
RefPtr<NativeBufferBlock> MakeNativeBufferBlockOfBytes() {
return FixedNativeBufferBlock<kSize>::New();
template<std::size_t kSize>
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>},
{"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 "
"is supported.",
FLAGS_tile_buffer_block_size);
}
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 "
"is supported.",
FLAGS_tile_buffer_block_size);
}
}
TILE_ON_INIT(0, InitializeMakeNativeBufferBlock);
} // namespace
}// namespace
RefPtr<NativeBufferBlock> MakeNativeBufferBlock() {
return make_native_buffer_block();
RefPtr<NativeBufferBlock>
MakeNativeBufferBlock()
{
return make_native_buffer_block();
}
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 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_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 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>> {
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 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_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 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>> {
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 kMaxIdle = std::chrono::seconds(10);
static constexpr auto kMinimumThreadCacheSize = 64; // 64M per thread.
static constexpr auto kTransferBatchSize = 16; // Extra 16M.
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 kMaxIdle = std::chrono::seconds(10);
static constexpr auto kMinimumThreadCacheSize = 64;// 64M per thread.
static constexpr auto kTransferBatchSize = 16;// Extra 16M.
};
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
}// namespace tile

View File

@ -8,34 +8,32 @@
#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;
virtual char *mutable_data() noexcept = 0;
};
RefPtr<NativeBufferBlock> MakeNativeBufferBlock();
template <typename F>
class ReferencingBufferBlock : public PolymorphicBufferBlock,
private F /* Empty Base Optimize */ {
template<typename F>
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)(); }
~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_; }
std::size_t size() const noexcept override { return size_; }
private:
const void *ptr_;
std::size_t size_;
}; // namespace tile
} // namespace tile
const void *ptr_;
std::size_t size_;
};// namespace tile
}// namespace tile
#endif // TILE_BASE_BUFFER_BUILTIN_BUFFER_BLOCK_H
#endif// TILE_BASE_BUFFER_BUILTIN_BUFFER_BLOCK_H

View File

@ -9,59 +9,63 @@
#include <vector>
namespace tile {
template <typename T> class CircularBuffer {
class UninitializedObject;
template<typename T>
class CircularBuffer {
class UninitializedObject;
public:
explicit CircularBuffer(std::size_t capacity);
explicit CircularBuffer(std::size_t capacity);
template <typename... Ts> bool Emplace(Ts &&...args) {
auto head = head_.load(std::memory_order_relaxed);
auto next = NormalizeIndex(head + 1);
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;
}
objects_[head].Initialize(std::forward<Ts>(args)...);
head_.store(next, std::memory_order_release);
return true;
}
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) {
objects->push_back(std::move(*objects_[current].Get()));
objects_[current].Destroy();
current = NormalizeIndex(current + 1);
}
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) {
objects->push_back(std::move(*objects_[current].Get()));
objects_[current].Destroy();
current = NormalizeIndex(current + 1);
tail_.store(current, std::memory_order_release);
}
tail_.store(current, std::memory_order_release);
}
private:
class UninitializedObject {
public:
T *Get() noexcept { return reinterpret_cast<T *>(&storage_); }
template <typename... Ts> void Initialize(Ts &&...args) {
new (Get()) T(std::forward<Ts>(args)...);
}
class UninitializedObject {
public:
T *Get() noexcept { return reinterpret_cast<T *>(&storage_); }
void Destroy() { Get()->~T(); }
template<typename... Ts>
void Initialize(Ts &&...args)
{
new (Get()) T(std::forward<Ts>(args)...);
}
private:
std::aligned_storage<sizeof(T), alignof(T)> storage_;
};
std::size_t NormalizeIndex(std::size_t x) {
return (x < capacity_) ? x : x - capacity_;
}
void Destroy() { Get()->~T(); }
private:
std::aligned_storage<sizeof(T), alignof(T)> storage_;
};
std::size_t NormalizeIndex(std::size_t x) { return (x < capacity_) ? x : x - capacity_; }
private:
std::size_t capacity_;
std::unique_ptr<UninitializedObject[]> objects_;
std::atomic<std::size_t> head_{}, tail_{};
std::size_t capacity_;
std::unique_ptr<UninitializedObject[]> objects_;
std::atomic<std::size_t> head_{}, tail_{};
};
} // namespace tile
}// namespace tile
#endif // _TILE_BASE_BUFFER_CIRCULAR_BUFFER_H
#endif// _TILE_BASE_BUFFER_CIRCULAR_BUFFER_H

View File

@ -2,42 +2,39 @@
namespace tile {
NoncontiguousBufferCompressionOutputStream::
NoncontiguousBufferCompressionOutputStream(
NoncontiguousBufferBuilder *builder)
: builder_(builder) {}
NoncontiguousBufferCompressionOutputStream::NoncontiguousBufferCompressionOutputStream(
NoncontiguousBufferBuilder *builder)
: builder_(builder)
{}
NoncontiguousBufferCompressionOutputStream::
~NoncontiguousBufferCompressionOutputStream() {
Flush();
NoncontiguousBufferCompressionOutputStream::~NoncontiguousBufferCompressionOutputStream() { Flush(); }
void
NoncontiguousBufferCompressionOutputStream::Flush()
{
if (using_bytes_ > 0) {
builder_->MarkWritten(using_bytes_);
using_bytes_ = 0;
}
}
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; }
if (using_bytes_) { builder_->MarkWritten(using_bytes_); }
*data = builder_->data();
*size = builder_->SizeAvailable();
using_bytes_ = *size;
TILE_CHECK(*size > 0);
return true;
}
bool NoncontiguousBufferCompressionOutputStream::Next(
void **data, std::size_t *size) noexcept {
if (!builder_) {
return false;
}
if (using_bytes_) {
builder_->MarkWritten(using_bytes_);
}
*data = builder_->data();
*size = builder_->SizeAvailable();
using_bytes_ = *size;
TILE_CHECK(*size > 0);
return true;
void
NoncontiguousBufferCompressionOutputStream::BackUp(std::size_t count) noexcept
{
using_bytes_ -= count;
}
void NoncontiguousBufferCompressionOutputStream::BackUp(
std::size_t count) noexcept {
using_bytes_ -= count;
}
} // namespace tile
}// namespace tile

View File

@ -6,22 +6,20 @@
#include "tile/base/compression/compression.h"
namespace tile {
class NoncontiguousBufferCompressionOutputStream
: public CompressionOutputStream {
class NoncontiguousBufferCompressionOutputStream : public CompressionOutputStream {
public:
explicit NoncontiguousBufferCompressionOutputStream(
NoncontiguousBufferBuilder *builder);
~NoncontiguousBufferCompressionOutputStream() override;
explicit NoncontiguousBufferCompressionOutputStream(NoncontiguousBufferBuilder *builder);
~NoncontiguousBufferCompressionOutputStream() override;
void Flush();
void Flush();
bool Next(void **data, std::size_t *size) noexcept override;
void BackUp(std::size_t count) noexcept override;
bool Next(void **data, std::size_t *size) noexcept override;
void BackUp(std::size_t count) noexcept override;
private:
std::size_t using_bytes_{};
NoncontiguousBufferBuilder *builder_;
std::size_t using_bytes_{};
NoncontiguousBufferBuilder *builder_;
};
} // namespace tile
}// namespace tile
#endif // TILE_BASE_BUFFER_COMPRESSION_OUTPUT_STREAM_H
#endif// TILE_BASE_BUFFER_COMPRESSION_OUTPUT_STREAM_H

View File

@ -8,14 +8,18 @@ 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) {
bb->Clear();
void
PoolTraits<PolymorphicBuffer>::OnPut(PolymorphicBuffer *bb)
{
bb->Clear();
}
namespace detail {
void PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p) {
TILE_DCHECK_EQ(p->UnsafeRefCount(), 0);
p->Destroy();
void
PolymorphicBufferBlockDeleter ::operator()(PolymorphicBufferBlock *p)
{
TILE_DCHECK_EQ(p->UnsafeRefCount(), 0);
p->Destroy();
}
} // namespace detail
} // namespace tile
}// namespace detail
}// namespace tile

View File

@ -19,102 +19,111 @@ 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; }
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;
PolymorphicBuffer(const PolymorphicBuffer &) = default;
PolymorphicBuffer &operator=(const PolymorphicBuffer &) = default;
PolymorphicBuffer() = default;
PolymorphicBuffer(const PolymorphicBuffer &) = default;
PolymorphicBuffer &operator=(const PolymorphicBuffer &) = default;
PolymorphicBuffer(PolymorphicBuffer &&other) noexcept
: ptr_(other.ptr_), size_(other.size_), ref_(std::move(other.ref_)) {
other.Clear();
}
PolymorphicBuffer &operator=(PolymorphicBuffer &&other) noexcept {
if (this != &other) {
ptr_ = other.ptr_;
size_ = other.size_;
ref_ = other.ref_;
other.Clear();
PolymorphicBuffer(PolymorphicBuffer &&other) noexcept
: ptr_(other.ptr_),
size_(other.size_),
ref_(std::move(other.ref_))
{
other.Clear();
}
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 &operator=(PolymorphicBuffer &&other) noexcept
{
if (this != &other) {
ptr_ = other.ptr_;
size_ = other.size_;
ref_ = other.ref_;
other.Clear();
}
return *this;
}
const char *data() const noexcept { return ptr_; }
std::size_t size() const noexcept { return size_; }
PolymorphicBuffer(RefPtr<PolymorphicBufferBlock> data, std::size_t start, std::size_t size)
: ptr_(data->data() + start),
size_(size),
ref_(std::move(data))
{}
void Skip(std::size_t bytes) {
TILE_CHECK_LT(bytes, size_);
size_ -= bytes;
ptr_ += bytes;
}
const char *data() const noexcept { return ptr_; }
void set_size(std::size_t size) {
TILE_DCHECK_LE(size, size_);
size_ = size;
}
std::size_t size() const noexcept { return 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());
void Skip(std::size_t bytes)
{
TILE_CHECK_LT(bytes, size_);
size_ -= bytes;
ptr_ += bytes;
}
ref_ = std::move(data);
ptr_ = data->data() + start;
size_ = size;
}
void set_size(std::size_t size)
{
TILE_DCHECK_LE(size, size_);
size_ = size;
}
void Clear() {
ptr_ = nullptr;
size_ = 0;
ref_ = nullptr;
}
void Reset(RefPtr<PolymorphicBufferBlock> data, std::size_t start, std::size_t size)
{
TILE_DCHECK_LE(start, size);
TILE_DCHECK_LE(size, data->size());
ref_ = std::move(data);
ptr_ = data->data() + start;
size_ = size;
}
void Clear()
{
ptr_ = nullptr;
size_ = 0;
ref_ = nullptr;
}
private:
friend class NoncontiguousBuffer;
friend class NoncontiguousBuffer;
internal::SinglyLinkedListEntry chain;
const char *ptr_{};
std::size_t size_{};
RefPtr<PolymorphicBufferBlock> ref_;
internal::SinglyLinkedListEntry chain;
const char *ptr_{};
std::size_t size_{};
RefPtr<PolymorphicBufferBlock> ref_;
};
namespace detail {
struct PolymorphicBufferBlockDeleter {
void operator()(PolymorphicBufferBlock *p);
void operator()(PolymorphicBufferBlock *p);
};
} // namespace detail
}// namespace detail
} // namespace tile
}// namespace tile
namespace tile {
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::chrono::seconds kMaxIdle = std::chrono::seconds(10);
static constexpr std::size_t kMinimumThreadCacheSize = 8192;
static constexpr std::size_t kTransferBatchSize = 1024;
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::chrono::seconds kMaxIdle = std::chrono::seconds(10);
static constexpr std::size_t kMinimumThreadCacheSize = 8192;
static constexpr std::size_t kTransferBatchSize = 1024;
static void OnPut(PolymorphicBuffer *bb);
static void OnPut(PolymorphicBuffer *bb);
};
} // namespace tile
#endif // TILE_BASE_BUFFER_POLYMORPHIC_BUFFER_H
}// namespace tile
#endif// TILE_BASE_BUFFER_POLYMORPHIC_BUFFER_H

View File

@ -14,280 +14,308 @@ namespace tile {
namespace {
PolymorphicBuffer MakeNativeBuffer(Slice s) {
auto buffer = MakeNativeBufferBlock();
memcpy(buffer->mutable_data(), s.data(), s.size());
return PolymorphicBuffer(buffer, 0, s.size());
PolymorphicBuffer
MakeNativeBuffer(Slice s)
{
auto buffer = MakeNativeBufferBlock();
memcpy(buffer->mutable_data(), s.data(), s.size());
return PolymorphicBuffer(buffer, 0, s.size());
}
} // namespace
}// namespace
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(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) {
NoncontiguousBuffer nb;
nb.Append(CreateBufferSlow("asdf"));
auto r = nb.Cut(3);
ASSERT_EQ(1, nb.ByteSize());
ASSERT_EQ("f", FlattenSlow(nb));
ASSERT_EQ(3, r.ByteSize());
ASSERT_EQ("asd", FlattenSlow(r));
TEST(NoncontiguousBuffer, Cut)
{
NoncontiguousBuffer nb;
nb.Append(CreateBufferSlow("asdf"));
auto r = nb.Cut(3);
ASSERT_EQ(1, nb.ByteSize());
ASSERT_EQ("f", FlattenSlow(nb));
ASSERT_EQ(3, r.ByteSize());
ASSERT_EQ("asd", FlattenSlow(r));
}
TEST(NoncontiguousBuffer, Cut1) {
NoncontiguousBuffer nb;
nb.Append(CreateBufferSlow("asdf"));
auto r = nb.Cut(4);
ASSERT_TRUE(nb.Empty());
ASSERT_EQ(4, r.ByteSize());
TEST(NoncontiguousBuffer, Cut1)
{
NoncontiguousBuffer nb;
nb.Append(CreateBufferSlow("asdf"));
auto r = nb.Cut(4);
ASSERT_TRUE(nb.Empty());
ASSERT_EQ(4, r.ByteSize());
}
TEST(NoncontiguousBuffer, Cut2) {
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asdf"));
nb.Append(MakeNativeBuffer("asdf"));
auto r = nb.Cut(4);
ASSERT_EQ(4, nb.ByteSize());
ASSERT_EQ(4, r.ByteSize());
TEST(NoncontiguousBuffer, Cut2)
{
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asdf"));
nb.Append(MakeNativeBuffer("asdf"));
auto r = nb.Cut(4);
ASSERT_EQ(4, nb.ByteSize());
ASSERT_EQ(4, r.ByteSize());
}
TEST(NoncontiguousBuffer, Cut3) {
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asdf"));
nb.Append(MakeNativeBuffer("asdf"));
auto r = nb.Cut(8);
ASSERT_TRUE(nb.Empty());
ASSERT_EQ(8, r.ByteSize());
TEST(NoncontiguousBuffer, Cut3)
{
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asdf"));
nb.Append(MakeNativeBuffer("asdf"));
auto r = nb.Cut(8);
ASSERT_TRUE(nb.Empty());
ASSERT_EQ(8, r.ByteSize());
}
TEST(NoncontiguousBuffer, Cut4) {
auto nb = CreateBufferSlow("asdfasf2345sfsdfdf");
auto nb2 = nb;
ASSERT_EQ(FlattenSlow(nb), FlattenSlow(nb2));
NoncontiguousBuffer splited;
splited.Append(nb.Cut(1));
splited.Append(nb.Cut(2));
splited.Append(nb.Cut(3));
splited.Append(nb.Cut(4));
splited.Append(std::move(nb));
ASSERT_EQ(FlattenSlow(nb2), FlattenSlow(splited));
TEST(NoncontiguousBuffer, Cut4)
{
auto nb = CreateBufferSlow("asdfasf2345sfsdfdf");
auto nb2 = nb;
ASSERT_EQ(FlattenSlow(nb), FlattenSlow(nb2));
NoncontiguousBuffer splited;
splited.Append(nb.Cut(1));
splited.Append(nb.Cut(2));
splited.Append(nb.Cut(3));
splited.Append(nb.Cut(4));
splited.Append(std::move(nb));
ASSERT_EQ(FlattenSlow(nb2), FlattenSlow(splited));
}
TEST(NoncontiguousBuffer, Skip) {
NoncontiguousBuffer splited;
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Skip(32);
ASSERT_EQ(0, splited.ByteSize());
TEST(NoncontiguousBuffer, Skip)
{
NoncontiguousBuffer splited;
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Append(CreateBufferSlow("asdf"));
splited.Skip(32);
ASSERT_EQ(0, splited.ByteSize());
}
TEST(NoncontiguousBuffer, Skip2) {
NoncontiguousBuffer buffer;
EXPECT_TRUE(buffer.Empty());
buffer.Skip(0); // Don't crash.
EXPECT_TRUE(buffer.Empty());
TEST(NoncontiguousBuffer, Skip2)
{
NoncontiguousBuffer buffer;
EXPECT_TRUE(buffer.Empty());
buffer.Skip(0);// Don't crash.
EXPECT_TRUE(buffer.Empty());
}
TEST(NoncontiguousBuffer, FlattenSlow) {
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asd4234"));
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
ASSERT_EQ("asd4234aXs", FlattenSlow(nb, 10));
TEST(NoncontiguousBuffer, FlattenSlow)
{
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asd4234"));
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
ASSERT_EQ("asd4234aXs", FlattenSlow(nb, 10));
}
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)));
nb.Append(MakeNativeBuffer(Slice("\x1", 1)));
nb.Append(MakeNativeBuffer(Slice("\x00\x00\x00", 3))); // Padding
C c;
FlattenToSlow(nb, &c, sizeof(C));
ASSERT_EQ(0xf0debc9a78563412, c.ll);
ASSERT_EQ(0x78563412, c.i);
ASSERT_EQ(true, c.f);
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)));
nb.Append(MakeNativeBuffer(Slice("\x1", 1)));
nb.Append(MakeNativeBuffer(Slice("\x00\x00\x00", 3)));// Padding
C c;
FlattenToSlow(nb, &c, sizeof(C));
ASSERT_EQ(0xf0debc9a78563412, c.ll);
ASSERT_EQ(0x78563412, c.i);
ASSERT_EQ(true, c.f);
}
TEST(NoncontiguousBuffer, FlattenSlowUntil) {
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asd4234"));
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX"));
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4"));
ASSERT_EQ("asd4234aXsdfsadfasdf2342", FlattenSlowUntil(nb, "2342"));
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "z", 5));
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "3", 5));
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "2", 5));
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4", 5));
TEST(NoncontiguousBuffer, FlattenSlowUntil)
{
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asd4234"));
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX"));
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4"));
ASSERT_EQ("asd4234aXsdfsadfasdf2342", FlattenSlowUntil(nb, "2342"));
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "z", 5));
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "3", 5));
ASSERT_EQ("asd42", FlattenSlowUntil(nb, "2", 5));
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4", 5));
}
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 "
"successfully.\r\nContent-Type: "
"application/x-protobuf\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 "
"OK\r\nRpc-Seq");
ASSERT_EQ("HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: "
"0\r\nRpc-Error-Reason: The operation completed "
"successfully.\r\nContent-Type: "
"application/x-protobuf\r\nContent-Length: 0\r\n\r\n",
FlattenSlowUntil(nb, "\r\n\r\n"));
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 "
"successfully.\r\nContent-Type: "
"application/x-protobuf\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 "
"OK\r\nRpc-Seq");
ASSERT_EQ("HTTP/1.1 200 OK\r\nRpc-SeqNo: 14563016719\r\nRpc-Error-Code: "
"0\r\nRpc-Error-Reason: The operation completed "
"successfully.\r\nContent-Type: "
"application/x-protobuf\r\nContent-Length: 0\r\n\r\n",
FlattenSlowUntil(nb, "\r\n\r\n"));
}
TEST(NoncontiguousBuffer, FlattenSlowUntil3) {
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asd4234"));
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX"));
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4"));
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "4aX"));
TEST(NoncontiguousBuffer, FlattenSlowUntil3)
{
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("asd4234"));
nb.Append(MakeNativeBuffer("aXsdfsadfasdf2342"));
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "aX"));
ASSERT_EQ("asd4", FlattenSlowUntil(nb, "4"));
ASSERT_EQ("asd4234aX", FlattenSlowUntil(nb, "4aX"));
}
TEST(NoncontiguousBuffer, FlattenSlowUntil4) {
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("AB"));
nb.Append(MakeNativeBuffer("CDEFGGGGHHHH"));
ASSERT_EQ("ABCDEFGGGG", FlattenSlowUntil(nb, "GGGG"));
TEST(NoncontiguousBuffer, FlattenSlowUntil4)
{
NoncontiguousBuffer nb;
nb.Append(MakeNativeBuffer("AB"));
nb.Append(MakeNativeBuffer("CDEFGGGGHHHH"));
ASSERT_EQ("ABCDEFGGGG", FlattenSlowUntil(nb, "GGGG"));
}
TEST(NoncontiguousBufferBuilder, Append) {
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer(""));
nbb.Append(MakeForeignBuffer("small"));
nbb.Append(MakeForeignBuffer(std::string(8192, 'a')));
nbb.Append(CreateBufferSlow(""));
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));
TEST(NoncontiguousBufferBuilder, Append)
{
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer(""));
nbb.Append(MakeForeignBuffer("small"));
nbb.Append(MakeForeignBuffer(std::string(8192, 'a')));
nbb.Append(CreateBufferSlow(""));
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));
}
TEST(NoncontiguousBufferBuilder, Reserve) {
auto temp_block = MakeNativeBufferBlock();
auto max_bytes = temp_block->size();
NoncontiguousBufferBuilder nbb;
auto ptr = nbb.data();
auto ptr2 = nbb.Reserve(10);
ASSERT_EQ(ptr, ptr2);
ASSERT_EQ(ptr + 10, nbb.data());
nbb.Append(std::string(max_bytes - 10 - 1, 'a'));
ptr = nbb.data();
ptr2 = nbb.Reserve(1); // Last byte in the block.
ASSERT_EQ(ptr, ptr2);
ASSERT_EQ(max_bytes, nbb.SizeAvailable());
TEST(NoncontiguousBufferBuilder, Reserve)
{
auto temp_block = MakeNativeBufferBlock();
auto max_bytes = temp_block->size();
NoncontiguousBufferBuilder nbb;
auto ptr = nbb.data();
auto ptr2 = nbb.Reserve(10);
ASSERT_EQ(ptr, ptr2);
ASSERT_EQ(ptr + 10, nbb.data());
nbb.Append(std::string(max_bytes - 10 - 1, 'a'));
ptr = nbb.data();
ptr2 = nbb.Reserve(1);// Last byte in the block.
ASSERT_EQ(ptr, ptr2);
ASSERT_EQ(max_bytes, nbb.SizeAvailable());
nbb.Append(std::string(max_bytes - 1, 'a'));
ptr = nbb.data();
ptr2 = nbb.Reserve(2);
ASSERT_NE(ptr, ptr2);
ASSERT_EQ(ptr2 + 2, nbb.data());
nbb.Append(std::string(max_bytes - 1, 'a'));
ptr = nbb.data();
ptr2 = nbb.Reserve(2);
ASSERT_NE(ptr, ptr2);
ASSERT_EQ(ptr2 + 2, nbb.data());
}
TEST(NoncontiguousBufferBuilder, DestructiveGet1) {
NoncontiguousBufferBuilder nbb;
nbb.Append("asdf1234", 6);
nbb.Append("1122", 4);
ASSERT_EQ("asdf12"
"1122",
FlattenSlow(nbb.DestructiveGet()));
TEST(NoncontiguousBufferBuilder, DestructiveGet1)
{
NoncontiguousBufferBuilder nbb;
nbb.Append("asdf1234", 6);
nbb.Append("1122", 4);
ASSERT_EQ("asdf12"
"1122",
FlattenSlow(nbb.DestructiveGet()));
}
TEST(NoncontiguousBufferBuilder, DestructiveGet2) {
NoncontiguousBufferBuilder nbb;
nbb.Append("aabbccd");
ASSERT_EQ("aabbccd", FlattenSlow(nbb.DestructiveGet()));
TEST(NoncontiguousBufferBuilder, DestructiveGet2)
{
NoncontiguousBufferBuilder nbb;
nbb.Append("aabbccd");
ASSERT_EQ("aabbccd", FlattenSlow(nbb.DestructiveGet()));
}
TEST(NoncontiguousBufferBuilder, DestructiveGet3) {
NoncontiguousBufferBuilder nbb;
nbb.Append(std::string(1000000, 'A'));
ASSERT_EQ(std::string(1000000, 'A'), FlattenSlow(nbb.DestructiveGet()));
TEST(NoncontiguousBufferBuilder, DestructiveGet3)
{
NoncontiguousBufferBuilder nbb;
nbb.Append(std::string(1000000, 'A'));
ASSERT_EQ(std::string(1000000, 'A'), FlattenSlow(nbb.DestructiveGet()));
}
TEST(NoncontiguousBufferBuilder, DestructiveGet4) {
NoncontiguousBufferBuilder nbb;
nbb.Append('c');
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
TEST(NoncontiguousBufferBuilder, DestructiveGet4)
{
NoncontiguousBufferBuilder nbb;
nbb.Append('c');
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
}
TEST(NoncontiguousBufferBuilder, DestructiveGet5) {
NoncontiguousBufferBuilder nbb;
nbb.Append(CreateBufferSlow("c"));
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
TEST(NoncontiguousBufferBuilder, DestructiveGet5)
{
NoncontiguousBufferBuilder nbb;
nbb.Append(CreateBufferSlow("c"));
ASSERT_EQ("c", FlattenSlow(nbb.DestructiveGet()));
}
TEST(NoncontiguousBufferBuilder, DestructiveGet6) {
NoncontiguousBufferBuilder nbb;
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(NoncontiguousBufferBuilder, DestructiveGet6)
{
NoncontiguousBufferBuilder nbb;
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) {
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeReferencingBuffer("abcdefg", 7));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
TEST(MakeReferencingBuffer, Simple)
{
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeReferencingBuffer("abcdefg", 7));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
}
TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized) {
int x = 0;
TEST(MakeReferencingBuffer, WithCallbackSmallBufferOptimized)
{
int x = 0;
NoncontiguousBufferBuilder nbb;
nbb.Append("aaa", 3);
// Small buffers are copied by `Append` and freed immediately.
nbb.Append(MakeReferencingBuffer("abcdefg", 7, [&] { ++x; }));
// Therefore the callback should have fired on return of `Append`.
EXPECT_EQ(1, x);
auto buffer = nbb.DestructiveGet();
EXPECT_EQ("aaaabcdefg", FlattenSlow(buffer));
}
TEST(MakeReferencingBuffer, WithCallback) {
static const std::string kBuffer(12345, 'a');
int x = 0;
{
NoncontiguousBufferBuilder nbb;
nbb.Append("aaa", 3);
nbb.Append(MakeReferencingBuffer(kBuffer.data(), 1024, [&] { ++x; }));
EXPECT_EQ(0, x);
// Small buffers are copied by `Append` and freed immediately.
nbb.Append(MakeReferencingBuffer("abcdefg", 7, [&] { ++x; }));
// Therefore the callback should have fired on return of `Append`.
EXPECT_EQ(1, x);
auto buffer = nbb.DestructiveGet();
EXPECT_EQ(0, x);
EXPECT_EQ("aaa" + kBuffer.substr(0, 1024), FlattenSlow(buffer));
}
EXPECT_EQ(1, x);
EXPECT_EQ("aaaabcdefg", FlattenSlow(buffer));
}
TEST(MakeForeignBuffer, String) {
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer(std::string("abcdefg")));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
TEST(MakeReferencingBuffer, WithCallback)
{
static const std::string kBuffer(12345, 'a');
int x = 0;
{
NoncontiguousBufferBuilder nbb;
nbb.Append("aaa", 3);
nbb.Append(MakeReferencingBuffer(kBuffer.data(), 1024, [&] { ++x; }));
EXPECT_EQ(0, x);
auto buffer = nbb.DestructiveGet();
EXPECT_EQ(0, x);
EXPECT_EQ("aaa" + kBuffer.substr(0, 1024), FlattenSlow(buffer));
}
EXPECT_EQ(1, x);
}
TEST(MakeForeignBuffer, VectorOfChar) {
std::vector<char> data{'a', 'b', 'c', 'd', 'e', 'f', 'g'};
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer(std::move(data)));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
TEST(MakeForeignBuffer, String)
{
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer(std::string("abcdefg")));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
}
TEST(MakeForeignBuffer, VectorOfChar)
{
std::vector<char> data{'a', 'b', 'c', 'd', 'e', 'f', 'g'};
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer(std::move(data)));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
}
// TEST(MakeForeignBuffer, VectorOfBytes) {
@ -299,13 +327,14 @@ TEST(MakeForeignBuffer, VectorOfChar) {
// EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
// }
TEST(MakeForeignBuffer, VectorOfUInt8) {
std::vector<std::uint8_t> data;
data.resize(7);
memcpy(data.data(), "abcdefg", 7);
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer(std::move(data)));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
TEST(MakeForeignBuffer, VectorOfUInt8)
{
std::vector<std::uint8_t> data;
data.resize(7);
memcpy(data.data(), "abcdefg", 7);
NoncontiguousBufferBuilder nbb;
nbb.Append(MakeForeignBuffer(std::move(data)));
EXPECT_EQ("abcdefg", FlattenSlow(nbb.DestructiveGet()));
}
} // namespace tile
}// namespace tile

View File

@ -41,86 +41,116 @@
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;
return true;
#else
return false;
return false;
#endif
}
inline bool IsHostBigEndian() {
inline bool
IsHostBigEndian()
{
#if TILE_BIG_ENDIAN == TILE_BYTE_ORDER
return true;
return true;
#else
return false;
return false;
#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;
return val_in_host;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
return ByteSwap16(val_in_host);
return ByteSwap16(val_in_host);
#else
#error "Unsupported byte order"
#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;
return val_in_host;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
return ByteSwap32(val_in_host);
return ByteSwap32(val_in_host);
#else
#error "Unsupported byte order"
#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;
return val_in_host;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
return ByteSwap64(val_in_host);
return ByteSwap64(val_in_host);
#else
#error "Unsupported byte order"
#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;
return val_in_network;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
return ByteSwap16(val_in_network);
return ByteSwap16(val_in_network);
#else
#error "Unsupported byte order"
#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;
return val_in_network;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
return ByteSwap32(val_in_network);
return ByteSwap32(val_in_network);
#else
#error "Unsupported byte order"
#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;
return val_in_network;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
return ByteSwap64(val_in_network);
return ByteSwap64(val_in_network);
#else
#error "Unsupported byte order"
#endif
}
} // namespace tile
}// namespace tile
#endif // TILE_BASE_BYTE_ORDER_H
#endif// TILE_BASE_BYTE_ORDER_H

View File

@ -8,109 +8,123 @@
namespace tile {
static union EndianHelper {
uint16_t v16;
uint32_t v32;
uint32_t v64;
uint8_t bytes[8];
} endian_helper = {.bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
uint16_t v16;
uint32_t v32;
uint32_t v64;
uint8_t bytes[8];
} endian_helper = {
.bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}
};
TEST(ByteOrder, ByteSwap16) {
const uint16_t val = 0x1234;
const uint16_t expected = 0x3412;
ASSERT_EQ(expected, ByteSwap16(val));
}
TEST(ByteOrder, ByteSwap32) {
const uint32_t val = 0x12345678;
const uint32_t expected = 0x78563412;
ASSERT_EQ(expected, ByteSwap32(val));
}
TEST(ByteOrder, ByteSwap64) {
const uint64_t val = 0x1234567890abcdef;
const uint64_t expected = 0xefcdab9078563412;
ASSERT_EQ(expected, ByteSwap64(val));
TEST(ByteOrder, ByteSwap16)
{
const uint16_t val = 0x1234;
const uint16_t expected = 0x3412;
ASSERT_EQ(expected, ByteSwap16(val));
}
TEST(ByteOrder, IsHostByteOrder) {
ASSERT_NE(IsHostBigEndian(), IsHostLittleEndian());
TEST(ByteOrder, ByteSwap32)
{
const uint32_t val = 0x12345678;
const uint32_t expected = 0x78563412;
ASSERT_EQ(expected, ByteSwap32(val));
}
TEST(ByteOrder, ByteSwap64)
{
const uint64_t val = 0x1234567890abcdef;
const uint64_t expected = 0xefcdab9078563412;
ASSERT_EQ(expected, ByteSwap64(val));
}
TEST(ByteOrder, IsHostByteOrder)
{
ASSERT_NE(IsHostBigEndian(), IsHostLittleEndian());
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
ASSERT_TRUE(IsHostBigEndian());
ASSERT_FALSE(IsHostLittleEndian());
ASSERT_TRUE(IsHostBigEndian());
ASSERT_FALSE(IsHostLittleEndian());
#else
ASSERT_TRUE(IsHostLittleEndian());
ASSERT_FALSE(IsHostBigEndian());
ASSERT_TRUE(IsHostLittleEndian());
ASSERT_FALSE(IsHostBigEndian());
#endif
}
TEST(ByteOrder, HostToNetwork16) {
const uint16_t val = 0x1234;
TEST(ByteOrder, HostToNetwork16)
{
const uint16_t val = 0x1234;
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
const uint16_t expected = val;
const uint16_t expected = val;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
const uint16_t expected = 0x3412;
const uint16_t expected = 0x3412;
#else
#error "Unsupported byte order"
#endif
ASSERT_EQ(expected, HostToNetwork16(val));
ASSERT_EQ(expected, HostToNetwork16(val));
}
TEST(ByteOrder, HostToNetwork32) {
const uint32_t val = 0x12345678;
TEST(ByteOrder, HostToNetwork32)
{
const uint32_t val = 0x12345678;
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
const uint32_t expected = val;
const uint32_t expected = val;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
const uint32_t expected = 0x78563412;
const uint32_t expected = 0x78563412;
#else
#error "Unsupported byte order"
#endif
ASSERT_EQ(expected, HostToNetwork32(val));
ASSERT_EQ(expected, HostToNetwork32(val));
}
TEST(ByteOrder, HostToNetwork64) {
const uint64_t val = 0x1234567890abcdef;
TEST(ByteOrder, HostToNetwork64)
{
const uint64_t val = 0x1234567890abcdef;
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
const uint64_t expected = val;
const uint64_t expected = val;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
const uint64_t expected = 0xefcdab9078563412;
const uint64_t expected = 0xefcdab9078563412;
#else
#error "Unsupported byte order"
#endif
ASSERT_EQ(expected, HostToNetwork64(val));
ASSERT_EQ(expected, HostToNetwork64(val));
}
TEST(ByteOrder, NetworkToHost16) {
const uint16_t val = 0x1234;
TEST(ByteOrder, NetworkToHost16)
{
const uint16_t val = 0x1234;
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
const uint16_t expected = val;
const uint16_t expected = val;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
const uint16_t expected = 0x3412;
const uint16_t expected = 0x3412;
#else
#error "Unsupported byte order"
#endif
ASSERT_EQ(expected, NetworkToHost16(val));
ASSERT_EQ(expected, NetworkToHost16(val));
}
TEST(ByteOrder, NetworkToHost32) {
const uint32_t val = 0x12345678;
TEST(ByteOrder, NetworkToHost32)
{
const uint32_t val = 0x12345678;
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
const uint32_t expected = val;
const uint32_t expected = val;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
const uint32_t expected = 0x78563412;
const uint32_t expected = 0x78563412;
#else
#error "Unsupported byte order"
#endif
ASSERT_EQ(expected, NetworkToHost32(val));
ASSERT_EQ(expected, NetworkToHost32(val));
}
TEST(ByteOrder, NetworkToHost64) {
const uint64_t val = 0x1234567890abcdef;
TEST(ByteOrder, NetworkToHost64)
{
const uint64_t val = 0x1234567890abcdef;
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
const uint64_t expected = val;
const uint64_t expected = val;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
const uint64_t expected = 0xefcdab9078563412;
const uint64_t expected = 0xefcdab9078563412;
#else
#error "Unsupported byte order"
#endif
ASSERT_EQ(expected, NetworkToHost64(val));
ASSERT_EQ(expected, NetworkToHost64(val));
}
} // namespace tile
}// namespace tile

View File

@ -12,178 +12,200 @@
namespace tile {
namespace detail {
// Has classof
template <typename T, typename Base> struct HasClassofImpl {
template <
typename TT, typename TBase,
typename = enable_if_t<std::is_same<
decltype(TT::classof(std::declval<const TBase &>())), bool>::value>>
static std::true_type test(int);
template <typename TT, typename TBase> static std::false_type test(...);
template<typename T, typename Base>
struct HasClassofImpl {
template<typename TT,
typename TBase,
typename = enable_if_t<std::is_same<decltype(TT::classof(std::declval<const TBase &>())), bool>::value>>
static std::true_type test(int);
template<typename TT, typename TBase>
static std::false_type test(...);
static constexpr bool value = decltype(test<T, Base>(0))::value;
static constexpr bool value = decltype(test<T, Base>(0))::value;
};
} // namespace detail
}// namespace detail
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> {
return T::classof(val);
}
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>
{
return T::classof(val);
}
template <typename Base>
static auto RuntimeTypeCheck(const Base &val)
-> enable_if_t<!detail::HasClassofImpl<T, Base>::value, bool> {
return dynamic_cast<const T *>(&val) != nullptr;
}
template<typename Base>
static auto RuntimeTypeCheck(const Base &val) -> enable_if_t<!detail::HasClassofImpl<T, Base>::value, bool>
{
return dynamic_cast<const T *>(&val) != nullptr;
}
};
class Castable {
class Dummy {};
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;
~Castable() = default;
template<typename, typename>
friend struct CastingTraits;
~Castable() = default;
template <typename T> void SetRuntimeTypeTo() {
SetRuntimeType(this, GetRuntimeTypeId<T>());
}
template<typename T>
void SetRuntimeTypeTo()
{
SetRuntimeType(this, GetRuntimeTypeId<T>());
}
template <typename T> int GetRuntimeType() const {
return GetRuntimeTypeId<T>();
}
template<typename T>
int GetRuntimeType() const
{
return GetRuntimeTypeId<T>();
}
template <typename T> static int GetRuntimeTypeId() {
static const int value = internal::IndexAlloc::For<Castable>()->Next();
return value;
}
template<typename T>
static int GetRuntimeTypeId()
{
static const int value = internal::IndexAlloc::For<Castable>()->Next();
return value;
}
private:
std::int_fast32_t type_ = GetRuntimeTypeId<Castable>();
std::int_fast32_t type_ = GetRuntimeTypeId<Castable>();
};
// ExactMatchCastable
class ExactMatchCastable : public Castable {
protected:
~ExactMatchCastable() = default;
~ExactMatchCastable() = default;
};
template <typename T>
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>();
}
template<typename T>
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) {
return CastingTraits<T>::RuntimeTypeCheck(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) {
return true;
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 *>;
template<typename T, typename Base>
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> {
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> {
return typeid(T) == typeid(*ptr);
template<typename T, typename Base>
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> void InvalidCast(Base *ptr) {
if (ReallyCastable<T, Base>(ptr)) {
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 "
"`ExactMatchCast`), or 2) the implementation of your `classof` is "
"incorrect.",
GetTypeName<T>());
} else {
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));
}
TILE_UNREACHABLE("");
template<typename T, typename Base>
auto
ReallyCastable(Base *ptr) -> enable_if_t<!std::has_virtual_destructor<T>::value, bool>
{
return typeid(T) == typeid(*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) {
return casting::detail::RuntimeTypeCheck<T>(val);
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 "
"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 "
"`ExactMatchCast`), or 2) the implementation of your `classof` is "
"incorrect.",
GetTypeName<T>());
} else {
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));
}
TILE_UNREACHABLE("");
}
template <typename T, typename Base> inline bool isa(const Base *val) {
return isa<T>(*val);
}// namespace detail
}// namespace casting
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,
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>
inline bool
isa(const Base *val)
{
return isa<T>(*val);
}
template <typename T, typename Base,
typename = enable_if_t<!std::is_pointer<Base>::value>>
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>>
inline auto
dyn_cast(Base &val) -> decltype(dyn_cast<T>(std::declval<Base *>())) {
return dyn_cast<T>(&val);
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)) {
return ptr ? dyn_cast<T>(ptr) : nullptr;
template<typename T, typename Base>
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);
}
casting::detail::InvalidCast<T>(ptr);
return 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); }
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 *>())) {
return cast<T>(&val);
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)) {
return ptr ? cast<T>(ptr) : nullptr;
template<typename T, typename Base>
inline auto
cast_or_null(Base *ptr) -> decltype(cast<T>(ptr))
{
return ptr ? cast<T>(ptr) : nullptr;
}
} // namespace tile
}// namespace tile
#endif // TILE_BASE_CASTING_H
#endif// TILE_BASE_CASTING_H

View File

@ -7,64 +7,63 @@
namespace tile {
struct Base {
virtual ~Base() = default;
enum { kA, kB, kC [[maybe_unused]] } type;
virtual ~Base() = default;
enum { kA, kB, kC [[maybe_unused]] } type;
};
struct A : Base {
A() { type = kA; }
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 {
B() { type = kB; }
B() { type = kB; }
static bool classof(const Base &val) { return val.type == kB; }
static bool classof(const Base &val) { return val.type == kB; }
};
auto pb = make_unique<B>();
auto pb = make_unique<B>();
Base *ptr = pb.get();
volatile A *converted_ptr;
struct C1 : ExactMatchCastable {};
struct C2 : C1 {
C2() { SetRuntimeType(this, GetRuntimeTypeId<C2>()); }
C2() { SetRuntimeType(this, GetRuntimeTypeId<C2>()); }
};
struct C3 : C1 {
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
};
auto pc2 = make_unique<C2>();
C1 *pc1 = pc2.get();
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);
} // namespace tile
}// namespace tile

View File

@ -9,95 +9,99 @@
namespace tile {
struct Base {
enum { kA, kB, kC } type;
enum { kA, kB, kC } type;
};
struct A : Base {
A() { type = kA; }
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 {
B() { type = kB; }
B() { type = kB; }
static bool classof(const Base &val) { return val.type == kB; }
static bool classof(const Base &val) { return val.type == kB; }
};
struct C : Base {
C() { type = kC; }
C() { type = kC; }
static bool classof(const Base &val) { return val.type == kC; }
static bool classof(const Base &val) { return val.type == kC; }
};
struct C1 : ExactMatchCastable {};
struct C2 : C1 {
C2() { SetRuntimeType(this, GetRuntimeTypeId<C2>()); }
C2() { SetRuntimeType(this, GetRuntimeTypeId<C2>()); }
};
struct C3 : C1 {
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
C3() { SetRuntimeType(this, GetRuntimeTypeId<C3>()); }
};
TEST(CastingDeathTest, InvalidCast) {
auto pa = make_unique<A>();
ASSERT_DEATH(cast<B>(pa.get()), "Invalid cast");
TEST(CastingDeathTest, InvalidCast)
{
auto pa = make_unique<A>();
ASSERT_DEATH(cast<B>(pa.get()), "Invalid cast");
}
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, Nullptr)
{
B *pb = nullptr;
ASSERT_EQ(nullptr, dyn_cast_or_null<A>(pb));
ASSERT_EQ(nullptr, cast_or_null<A>(pb));
}
TEST(Casting, DownCastFailure) {
auto pa = make_unique<A>();
ASSERT_EQ(nullptr, dyn_cast<B>(pa.get()));
TEST(Casting, DownCastFailure)
{
auto pa = make_unique<A>();
ASSERT_EQ(nullptr, dyn_cast<B>(pa.get()));
}
TEST(Casting, Cast) {
auto pb = make_unique<B>();
Base *ptr = pb.get();
TEST(Casting, Cast)
{
auto pb = make_unique<B>();
Base *ptr = pb.get();
ASSERT_NE(nullptr, dyn_cast<Base>(ptr));
ASSERT_NE(nullptr, dyn_cast<A>(ptr));
ASSERT_NE(nullptr, dyn_cast<B>(ptr));
ASSERT_NE(nullptr, dyn_cast<Base>(ptr));
ASSERT_NE(nullptr, dyn_cast<A>(ptr));
ASSERT_NE(nullptr, dyn_cast<B>(ptr));
ASSERT_NE(nullptr, cast<A>(ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<B>(ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<A>(*ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<B>(*ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<A>(ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<B>(ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<A>(*ptr));// Casting pointer.
ASSERT_NE(nullptr, cast<B>(*ptr));// Casting pointer.
ASSERT_NE(nullptr, dyn_cast<B>(pb.get())); // Cast to self.
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
ASSERT_NE(nullptr, dyn_cast<B>(pb.get()));// Cast to self.
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
}
TEST(Casting, CastWithConst) {
auto pb = make_unique<const B>();
const Base *ptr = pb.get();
TEST(Casting, CastWithConst)
{
auto pb = make_unique<const B>();
const Base *ptr = pb.get();
ASSERT_NE(nullptr, dyn_cast<Base>(ptr));
ASSERT_NE(nullptr, dyn_cast<A>(ptr));
ASSERT_NE(nullptr, dyn_cast<B>(ptr));
ASSERT_NE(nullptr, dyn_cast<Base>(ptr));
ASSERT_NE(nullptr, dyn_cast<A>(ptr));
ASSERT_NE(nullptr, dyn_cast<B>(ptr));
ASSERT_NE(nullptr, cast<A>(ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<B>(ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<A>(*ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<B>(*ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<A>(ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<B>(ptr)); // Casting pointer.
ASSERT_NE(nullptr, cast<A>(*ptr));// Casting pointer.
ASSERT_NE(nullptr, cast<B>(*ptr));// Casting pointer.
ASSERT_NE(nullptr, dyn_cast<B>(pb.get())); // Cast to self.
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
ASSERT_NE(nullptr, dyn_cast<B>(pb.get()));// Cast to self.
ASSERT_EQ(nullptr, dyn_cast<C>(ptr)); // Cast failure.
}
TEST(Casting, ExactMatchCastable) {
auto pc2 = make_unique<C2>();
C1 *p = pc2.get();
ASSERT_NE(nullptr, dyn_cast<C1>(p)); // Success.
ASSERT_NE(nullptr, dyn_cast<C2>(p)); // Success.
ASSERT_EQ(nullptr, dyn_cast<C3>(p)); // Failure.
TEST(Casting, ExactMatchCastable)
{
auto pc2 = make_unique<C2>();
C1 *p = pc2.get();
ASSERT_NE(nullptr, dyn_cast<C1>(p));// Success.
ASSERT_NE(nullptr, dyn_cast<C2>(p));// Success.
ASSERT_EQ(nullptr, dyn_cast<C3>(p));// Failure.
}
} // namespace tile
}// namespace tile

View File

@ -15,132 +15,150 @@
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> {
static constexpr auto value =
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::seconds(1716543273));
template<>
struct Epoch<std::chrono::system_clock> {
static constexpr auto value =
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1716543273));
};
template <typename Clock>
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));
return typename Clock::time_point(duration);
template<typename Clock>
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));
return typename Clock::time_point(duration);
}
void Sleep(long ns) {
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = (ns + 999) / 1000;
void
Sleep(long ns)
{
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = (ns + 999) / 1000;
select(0, NULL, NULL, NULL, &timeout);
select(0, NULL, NULL, NULL, &timeout);
}
} // namespace
}// namespace
namespace detail {
namespace chrono {
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;
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() {
async_updated_timestamps.steady_clock_time_since_epoch.store(
ReadSteadyClock().time_since_epoch(),
// std::chrono::steady_clock::now().time_since_epoch(),
std::memory_order_relaxed);
async_updated_timestamps.system_clock_time_since_epoch.store(
ReadSystemClock().time_since_epoch(),
// std::chrono::system_clock::now().time_since_epoch(),
std::memory_order_relaxed);
void
UpdateCoarseTimestamps()
{
async_updated_timestamps.steady_clock_time_since_epoch.store(
ReadSteadyClock().time_since_epoch(),
// std::chrono::steady_clock::now().time_since_epoch(),
std::memory_order_relaxed);
async_updated_timestamps.system_clock_time_since_epoch.store(
ReadSystemClock().time_since_epoch(),
// std::chrono::system_clock::now().time_since_epoch(),
std::memory_order_relaxed);
}
constexpr std::chrono::microseconds CoarseClockInitializer::kAccuracy;
CoarseClockInitializer *CoarseClockInitializer::Instance() {
static NeverDestroyedSingleton<CoarseClockInitializer> cci;
return cci.Get();
CoarseClockInitializer *
CoarseClockInitializer::Instance()
{
static NeverDestroyedSingleton<CoarseClockInitializer> cci;
return cci.Get();
}
void CoarseClockInitializer::Start() {
if (!internal::TestAndSet(running_, false, true)) {
TILE_LOG_WARNING("CoarseClockInitializer is already running");
return;
}
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);
while (running_.load(std::memory_order_relaxed)) {
// std::this_thread::sleep_for(std::chrono::nanoseconds(500));
UpdateCoarseTimestamps();
// Sleep(accuracy_as_ns / 2);
std::this_thread::sleep_for(kAccuracy / 2);
void
CoarseClockInitializer::Start()
{
if (!internal::TestAndSet(running_, false, true)) {
TILE_LOG_WARNING("CoarseClockInitializer is already running");
return;
}
});
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);
while (running_.load(std::memory_order_relaxed)) {
// std::this_thread::sleep_for(std::chrono::nanoseconds(500));
UpdateCoarseTimestamps();
// Sleep(accuracy_as_ns / 2);
std::this_thread::sleep_for(kAccuracy / 2);
}
});
}
void CoarseClockInitializer::Stop() {
running_.store(false, std::memory_order_relaxed);
void
CoarseClockInitializer::Stop()
{
running_.store(false, std::memory_order_relaxed);
}
void CoarseClockInitializer::Join() {
if (worker_) {
worker_->join();
worker_.reset();
}
void
CoarseClockInitializer::Join()
{
if (worker_) {
worker_->join();
worker_.reset();
}
}
CoarseClockInitializer::CoarseClockInitializer() {}
CoarseClockInitializer::~CoarseClockInitializer() {}
} // namespace chrono
} // namespace detail
}// namespace chrono
}// namespace detail
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() {
// return ReadClock<std::chrono::system_clock>(CLOCK_REALTIME);
return std::chrono::system_clock::now();
std::chrono::steady_clock::time_point
ReadSteadyClock()
{
// return ReadClock<std::chrono::steady_clock>(CLOCK_MONOTONIC);
return std::chrono::steady_clock::now();
}
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);
return std::chrono::steady_clock::time_point(target_duration);
std::chrono::system_clock::time_point
ReadSystemClock()
{
// return ReadClock<std::chrono::system_clock>(CLOCK_REALTIME);
return std::chrono::system_clock::now();
}
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);
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);
return std::chrono::steady_clock::time_point(target_duration);
}
} // namespace tile
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);
}
}// namespace tile

View File

@ -17,26 +17,26 @@ namespace chrono {
class CoarseClockInitializer {
public:
static constexpr auto kAccuracy = std::chrono::microseconds(500);
static CoarseClockInitializer *Instance();
static constexpr auto kAccuracy = std::chrono::microseconds(500);
static CoarseClockInitializer *Instance();
// for `tile::Start()`
void Start();
void Stop();
void Join();
// for `tile::Start()`
void Start();
void Stop();
void Join();
private:
~CoarseClockInitializer();
CoarseClockInitializer();
friend NeverDestroyedSingleton<CoarseClockInitializer>;
~CoarseClockInitializer();
CoarseClockInitializer();
friend NeverDestroyedSingleton<CoarseClockInitializer>;
private:
std::unique_ptr<std::thread> worker_;
std::atomic<bool> running_{false};
std::unique_ptr<std::thread> worker_;
std::atomic<bool> running_{false};
};
} // namespace chrono
} // namespace detail
}// namespace chrono
}// namespace detail
std::chrono::steady_clock::time_point ReadSteadyClock();
std::chrono::system_clock::time_point ReadSystemClock();
@ -45,10 +45,13 @@ 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() {
return ReadCoarseSystemClock().time_since_epoch() / std::chrono::seconds(1);
inline std::int64_t
ReadUnixTimestamp()
{
return ReadCoarseSystemClock().time_since_epoch() / std::chrono::seconds(1);
}
} // namespace tile
}// namespace tile
#endif // _TILE_BASE_CHRONO_H
#endif// _TILE_BASE_CHRONO_H

View File

@ -8,63 +8,67 @@
namespace tile {
void Benchmark_GetTimeOfDay(benchmark::State &state) {
while (state.KeepRunning()) {
struct timeval tv;
gettimeofday(&tv, nullptr);
}
void
Benchmark_GetTimeOfDay(benchmark::State &state)
{
while (state.KeepRunning()) {
struct timeval tv;
gettimeofday(&tv, nullptr);
}
}
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) {
while (state.KeepRunning()) {
// ... <+23>: lea 0x137c5a(%rip),%rax # `system_clock_time_since_epoch`
// ... <+30>: mov (%rax),%rax
ReadCoarseSteadyClock();
}
void
Benchmark_ReadCoarseSteadyClock(benchmark::State &state)
{
while (state.KeepRunning()) {
// ... <+23>: lea 0x137c5a(%rip),%rax # `system_clock_time_since_epoch`
// ... <+30>: mov (%rax),%rax
ReadCoarseSteadyClock();
}
}
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);
} // namespace tile
}// namespace tile

View File

@ -7,42 +7,38 @@ 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 double total = 0;
for (std::size_t i = 0; i != n; ++i) {
total += 1.0f / n * f();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return static_cast<long>(total);
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();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return static_cast<long>(total);
}
TEST(SystemClock, Compare) {
auto diff = AvageTime([] {
return (ReadSystemClock() - std::chrono::system_clock::now()) / one_ms;
});
ASSERT_NEAR(diff, 0, 5);
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;
});
ASSERT_NEAR(diff, 0, 5);
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;
});
ASSERT_NEAR(diff, 0, kTestN);
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;
});
ASSERT_NEAR(diff, 0, kTestN);
TEST(CoarseSteadyClock, Compare)
{
auto diff = AvageTime([] { return (ReadCoarseSteadyClock() - std::chrono::steady_clock::now()) / one_ms; });
ASSERT_NEAR(diff, 0, kTestN);
}
} // namespace tile
}// namespace tile

View File

@ -4,89 +4,96 @@
namespace tile {
std::unique_ptr<Decompressor> MakeDecompressor(Slice name) {
return decompressor_registry.TryNew(name);
std::unique_ptr<Decompressor>
MakeDecompressor(Slice name)
{
return decompressor_registry.TryNew(name);
}
std::unique_ptr<Compressor> MakeCompressor(Slice name) {
return compressor_registry.TryNew(name);
std::unique_ptr<Compressor>
MakeCompressor(Slice name)
{
return compressor_registry.TryNew(name);
}
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;
}
return builder.DestructiveGet();
std::optional<NoncontiguousBuffer>
Compress(Compressor *compressor, const NoncontiguousBuffer &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;
}
NoncontiguousBufferCompressionOutputStream out(builder);
return compressor->Compress(nb, &out);
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, Slice body,
NoncontiguousBufferBuilder *builder) {
if (!compressor) {
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
return false;
}
bool
Compress(Compressor *compressor, const NoncontiguousBuffer &nb, NoncontiguousBufferBuilder *builder)
{
if (!compressor) {
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
return false;
}
NoncontiguousBufferCompressionOutputStream out(builder);
return compressor->Compress(body.data(), body.size(), &out);
NoncontiguousBufferCompressionOutputStream out(builder);
return compressor->Compress(nb, &out);
}
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;
}
return builder.DestructiveGet();
bool
Compress(Compressor *compressor, Slice body, NoncontiguousBufferBuilder *builder)
{
if (!compressor) {
TILE_LOG_WARNING_EVERY_SECOND("Compressor nullptr");
return false;
}
NoncontiguousBufferCompressionOutputStream out(builder);
return compressor->Compress(body.data(), body.size(), &out);
}
bool Decompress(Decompressor *decompressor, const NoncontiguousBuffer &nb,
NoncontiguousBufferBuilder *builder) {
if (!decompressor) {
TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr");
return false;
}
NoncontiguousBufferCompressionOutputStream out(builder);
return decompressor->Decompress(nb, &out);
}
bool Decompress(Decompressor *decompressor, Slice body,
NoncontiguousBufferBuilder *builder) {
if (!decompressor) {
TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr");
return false;
}
NoncontiguousBufferCompressionOutputStream out(builder);
return decompressor->Decompress(body.data(), body.size(), &out);
std::optional<NoncontiguousBuffer>
Decompress(Decompressor *decompressor, const NoncontiguousBuffer &body)
{
NoncontiguousBufferBuilder builder;
if (!Decompress(decompressor, body, &builder)) { return std::nullopt; }
return builder.DestructiveGet();
}
} // namespace tile
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;
}
NoncontiguousBufferCompressionOutputStream out(builder);
return decompressor->Decompress(nb, &out);
}
bool
Decompress(Decompressor *decompressor, Slice body, NoncontiguousBufferBuilder *builder)
{
if (!decompressor) {
TILE_LOG_WARNING_EVERY_SECOND("Decompressor nullptr");
return false;
}
NoncontiguousBufferCompressionOutputStream out(builder);
return decompressor->Decompress(body.data(), body.size(), &out);
}
}// namespace tile

View File

@ -11,26 +11,19 @@ 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
}// namespace tile
#endif // TILE_BASE_COMPRESSION_H
#endif// TILE_BASE_COMPRESSION_H

View File

@ -4,25 +4,33 @@ 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_;
*data = internal::RemoveConstPtr(buffer_->data()) + using_bytes_;
*size = every_size_;
using_bytes_ += every_size_;
return true;
using_bytes_ += every_size_;
return true;
}
void TestCompressionOutputStream::BackUp(std::size_t count) noexcept {
using_bytes_ -= count;
}
void TestCompressionOutputStream::Flush() { buffer_->resize(using_bytes_); }
} // namespace tile
void
TestCompressionOutputStream::BackUp(std::size_t count) noexcept
{
using_bytes_ -= count;
}
void
TestCompressionOutputStream::Flush()
{
buffer_->resize(using_bytes_);
}
}// namespace tile

View File

@ -9,57 +9,50 @@
namespace tile {
class CompressionOutputStream {
public:
virtual ~CompressionOutputStream() = default;
// Get a buffer to write to. The buffer may be backed by multiple
virtual bool Next(void **data, std::size_t *size) noexcept = 0;
virtual void BackUp(std::size_t count) noexcept = 0;
virtual ~CompressionOutputStream() = default;
// Get a buffer to write to. The buffer may be backed by multiple
virtual bool Next(void **data, std::size_t *size) noexcept = 0;
virtual void BackUp(std::size_t count) noexcept = 0;
};
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 ~Compressor() = default;
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;
~Decompressor() = default;
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);
TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(decompressor_registry, Decompressor);
} // namespace tile
}// namespace tile
#define TILE_COMPRESSION_REGISTER_COMPRESSOR(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)
#define TILE_COMPRESSION_REGISTER_COMPRESSOR(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)
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;
void Flush();
bool Next(void **data, std::size_t *size) noexcept override;
void BackUp(std::size_t count) noexcept override;
void Flush();
private:
std::size_t using_bytes_{};
std::size_t every_size_;
std::string *buffer_;
std::size_t using_bytes_{};
std::size_t every_size_;
std::string *buffer_;
};
} // namespace tile
}// namespace tile
#endif // TILE_BASE_COMPRESSION_COMPRESSION_H
#endif// TILE_BASE_COMPRESSION_COMPRESSION_H

View File

@ -11,24 +11,28 @@ TILE_COMPRESSION_REGISTER_DECOMPRESSOR("gzip", GzipDecompressor);
namespace detail {
struct ZStream {
z_stream stream;
z_stream stream;
};
} // namespace detail
}// namespace detail
namespace {
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;
return rate * kMinRate;
}
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;
return rate * kMinRate;
}
return default_value;
return default_value;
}
inline uint32_t RestrictAvailSize(size_t size) {
return static_cast<uint32_t>(std::min(size, static_cast<size_t>(UINT32_MAX)));
inline uint32_t
RestrictAvailSize(size_t size)
{
return static_cast<uint32_t>(std::min(size, static_cast<size_t>(UINT32_MAX)));
}
// See document of inflateInit2 in zlib.h
@ -37,207 +41,223 @@ 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) {
TILE_CHECK(stream && out_stream);
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)) {
// no input data
return true;
}
if (!finish && (input_buffer == nullptr || input_size == 0)) {
// no input data
return true;
}
stream->next_in = reinterpret_cast<Bytef *>(const_cast<void *>(input_buffer));
int code = Z_OK;
// the number of `buffer too small error`
int need_more_space_cnt = 0;
std::string tmp_buffer;
size_t left_size = input_size;
while (left_size > 0 || need_more_space_cnt > 0 || finish) {
stream->avail_in = RestrictAvailSize(left_size);
const auto current_avail_in = stream->avail_in;
std::size_t out_size;
if (!need_more_space_cnt) {
void *out_data;
if (!out_stream->Next(&out_data, &out_size)) {
// out buffer is full
return false;
}
stream->next_in = reinterpret_cast<Bytef *>(const_cast<void *>(input_buffer));
int code = Z_OK;
// the number of `buffer too small error`
int need_more_space_cnt = 0;
std::string tmp_buffer;
size_t left_size = input_size;
while (left_size > 0 || need_more_space_cnt > 0 || finish) {
stream->avail_in = RestrictAvailSize(left_size);
const auto current_avail_in = stream->avail_in;
std::size_t out_size;
if (!need_more_space_cnt) {
void *out_data;
if (!out_stream->Next(&out_data, &out_size)) {
// out buffer is full
return false;
}
stream->next_out = reinterpret_cast<Bytef *>(out_data);
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()));
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.");
int flush_option = finish ? Z_FINISH : Z_NO_FLUSH;
if (is_deflate) {
code = deflate(stream, flush_option);
} else {
code = inflate(stream, flush_option);
}
// 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 (stream->avail_in == 0) {
// if not finish, we need more input data
// otherwise, fail to compress/decompress
return !finish;
}
++need_more_space_cnt;
continue;
}
if (code < 0) {
TILE_LOG_ERROR_EVERY_SECOND("gzip compress error code [{}]", code);
break;
}
// consume input data (current_avail_in - stream->avail_in)
// produce output data (stream->avail_out)
left_size -= current_avail_in - stream->avail_in;
if (need_more_space_cnt == 0) {
out_stream->BackUp(stream->avail_out);
} else {
if (TILE_UNLIKELY(!CopyDataToCompressionOutputStream(
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; }
}
return code == Z_OK;
}
}// namespace
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 ok = Init(out);
std::size_t left = bytes.ByteSize();
for (auto iter = bytes.begin(); ok && iter != bytes.end(); ++iter) {
auto len = std::min(left, iter->size());
ok &= Append(iter->data(), len);
left -= len;
}
ok &= Flush();
out_ = nullptr;
return ok;
}
bool
GzipCompressor::Append(const void *buffer, std::size_t size)
{
return DoAppend(&stream_->stream, out_, buffer, size, true, false);
}
bool
GzipCompressor::Flush()
{
TILE_CHECK(out_);
if (!DoAppend(&stream_->stream, out_, nullptr, 0, true, true)) { return false; }
return Release();
}
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);
if (code != Z_OK) {
TILE_LOG_ERROR_EVERY_SECOND("deflateInit2 error code [{}]", code);
stream_ = nullptr;
} 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()));
out_size = tmp_buffer.size();
out_ = out;
}
return code == Z_OK;
}
stream->avail_out = RestrictAvailSize(out_size);
const std::size_t current_avail_out = stream->avail_out;
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); }
return code == Z_OK;
}
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);
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 ok = Init(out);
std::size_t left = compressed.ByteSize();
for (auto iter = compressed.begin(); ok && iter != compressed.end(); ++iter) {
auto len = std::min(left, iter->size());
ok &= Append(iter->data(), len);
left -= len;
}
ok &= Flush();
out_ = nullptr;
return ok;
}
bool
GzipDecompressor::Append(const void *buffer, std::size_t size)
{
return DoAppend(&stream_->stream, out_, buffer, size, false, false);
}
bool
GzipDecompressor::Flush()
{
TILE_CHECK(out_);
if (!DoAppend(&stream_->stream, out_, nullptr, 0, false, true)) { return false; }
return Release();
}
bool
GzipDecompressor::Init(CompressionOutputStream *out)
{
TILE_CHECK(!out_);
stream_ = make_unique<detail::ZStream>();
int code = inflateInit2(&stream_->stream, MAX_WBITS + ZLIB_INIT_FLAG_GZIP);
if (code != Z_OK) {
TILE_LOG_ERROR_EVERY_SECOND("inflateInit2 error code [{}]", code);
stream_ = nullptr;
} else {
code = inflate(stream, flush_option);
out_ = out;
}
// 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 (stream->avail_in == 0) {
// if not finish, we need more input data
// otherwise, fail to compress/decompress
return !finish;
}
++need_more_space_cnt;
continue;
}
if (code < 0) {
TILE_LOG_ERROR_EVERY_SECOND("gzip compress error code [{}]", code);
break;
}
// consume input data (current_avail_in - stream->avail_in)
// produce output data (stream->avail_out)
left_size -= current_avail_in - stream->avail_in;
if (need_more_space_cnt == 0) {
out_stream->BackUp(stream->avail_out);
} else {
if (TILE_UNLIKELY(!CopyDataToCompressionOutputStream(
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;
}
}
return code == Z_OK;
}
} // namespace
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 ok = Init(out);
std::size_t left = bytes.ByteSize();
for (auto iter = bytes.begin(); ok && iter != bytes.end(); ++iter) {
auto len = std::min(left, iter->size());
ok &= Append(iter->data(), len);
left -= len;
}
ok &= Flush();
out_ = nullptr;
return ok;
}
bool GzipCompressor::Append(const void *buffer, std::size_t size) {
return DoAppend(&stream_->stream, out_, buffer, size, true, false);
}
bool GzipCompressor::Flush() {
TILE_CHECK(out_);
if (!DoAppend(&stream_->stream, out_, nullptr, 0, true, true)) {
return false;
}
return Release();
return code == Z_OK;
}
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);
if (code != Z_OK) {
TILE_LOG_ERROR_EVERY_SECOND("deflateInit2 error code [{}]", code);
stream_ = nullptr;
} else {
out_ = out;
}
return code == Z_OK;
}
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);
}
return code == Z_OK;
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); }
return code == Z_OK;
}
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 ok = Init(out);
std::size_t left = compressed.ByteSize();
for (auto iter = compressed.begin(); ok && iter != compressed.end(); ++iter) {
auto len = std::min(left, iter->size());
ok &= Append(iter->data(), len);
left -= len;
}
ok &= Flush();
out_ = nullptr;
return ok;
}
bool GzipDecompressor::Append(const void *buffer, std::size_t size) {
return DoAppend(&stream_->stream, out_, buffer, size, false, false);
}
bool GzipDecompressor::Flush() {
TILE_CHECK(out_);
if (!DoAppend(&stream_->stream, out_, nullptr, 0, false, true)) {
return false;
}
return Release();
}
bool GzipDecompressor::Init(CompressionOutputStream *out) {
TILE_CHECK(!out_);
stream_ = make_unique<detail::ZStream>();
int code = inflateInit2(&stream_->stream, MAX_WBITS + ZLIB_INIT_FLAG_GZIP);
if (code != Z_OK) {
TILE_LOG_ERROR_EVERY_SECOND("inflateInit2 error code [{}]", code);
stream_ = nullptr;
} else {
out_ = out;
}
return code == Z_OK;
}
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);
}
return code == Z_OK;
}
} // namespace compression
} // namespace tile
}// namespace compression
}// namespace tile

View File

@ -5,48 +5,43 @@
#include <memory>
#include "tile/base/compression/compression.h"
namespace tile {
namespace compression {
namespace detail {
struct ZStream;
} // namespace detail
}// namespace detail
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);
bool Flush();
bool Init(CompressionOutputStream *out);
bool Release();
std::unique_ptr<detail::ZStream> stream_;
CompressionOutputStream *out_ = nullptr;
bool Append(const void *buffer, std::size_t size);
bool Flush();
bool Init(CompressionOutputStream *out);
bool Release();
std::unique_ptr<detail::ZStream> stream_;
CompressionOutputStream *out_ = nullptr;
};
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);
bool Flush();
bool Init(CompressionOutputStream *out);
bool Release();
std::unique_ptr<detail::ZStream> stream_;
CompressionOutputStream *out_ = nullptr;
bool Append(const void *buffer, std::size_t size);
bool Flush();
bool Init(CompressionOutputStream *out);
bool Release();
std::unique_ptr<detail::ZStream> stream_;
CompressionOutputStream *out_ = nullptr;
};
} // namespace compression
} // namespace tile
}// namespace compression
}// namespace tile
#endif // TILE_BASE_COMPRESSION_GZIP_H
#endif// TILE_BASE_COMPRESSION_GZIP_H

View File

@ -1,37 +1,33 @@
#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;
std::size_t current_pos = 0;
std::size_t left_to_copy = size;
while (true) {
void *next_data;
std::size_t next_size;
if (TILE_UNLIKELY(!out->Next(&next_data, &next_size))) {
return false;
while (true) {
void *next_data;
std::size_t next_size;
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);
out->BackUp(next_size - left_to_copy);
return true;
} else {
memcpy(next_data, reinterpret_cast<const char *>(data) + current_pos, next_size);
current_pos += next_size;
left_to_copy -= next_size;
}
}
if (left_to_copy <= next_size) {
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);
current_pos += next_size;
left_to_copy -= next_size;
}
}
TILE_UNREACHABLE("");
return false;
TILE_UNREACHABLE("");
return false;
}
} // namespace compression
} // namespace tile
}// namespace compression
}// namespace tile

View File

@ -7,9 +7,8 @@
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
}// namespace tile
#endif // TILE_BASE_COMPRESSION_UTIL_H
#endif// TILE_BASE_COMPRESSION_UTIL_H

View File

@ -4,19 +4,20 @@
namespace tile {
namespace compression {
TEST(CopyDataToCompressionOutputStream, All) {
std::string s;
std::string a = "123456789+";
TEST(CopyDataToCompressionOutputStream, All)
{
std::string s;
std::string a = "123456789+";
TestCompressionOutputStream out1(&s, 2);
CopyDataToCompressionOutputStream(&out1, a.data(), 1);
CopyDataToCompressionOutputStream(&out1, a.data() + 1, 2);
CopyDataToCompressionOutputStream(&out1, a.data() + 3, 3);
CopyDataToCompressionOutputStream(&out1, a.data() + 6, 4);
out1.Flush();
TestCompressionOutputStream out1(&s, 2);
CopyDataToCompressionOutputStream(&out1, a.data(), 1);
CopyDataToCompressionOutputStream(&out1, a.data() + 1, 2);
CopyDataToCompressionOutputStream(&out1, a.data() + 3, 3);
CopyDataToCompressionOutputStream(&out1, a.data() + 6, 4);
out1.Flush();
ASSERT_EQ(s, a);
ASSERT_EQ(s, a);
}
} // namespace compression
}// namespace compression
} // namespace tile
}// namespace tile

View File

@ -11,62 +11,67 @@ static const char *algos[] = {
"gzip",
};
}
TEST(MakeCompressor, All) {
auto &&c = MakeCompressor("gzip");
EXPECT_TRUE(c);
c = MakeCompressor("??");
EXPECT_FALSE(c);
TEST(MakeCompressor, All)
{
auto &&c = MakeCompressor("gzip");
EXPECT_TRUE(c);
c = MakeCompressor("??");
EXPECT_FALSE(c);
}
TEST(MakeDecompressor, All) {
auto &&c = MakeDecompressor("gzip");
EXPECT_TRUE(c);
c = MakeDecompressor("??");
EXPECT_FALSE(c);
TEST(MakeDecompressor, All)
{
auto &&c = MakeDecompressor("gzip");
EXPECT_TRUE(c);
c = MakeDecompressor("??");
EXPECT_FALSE(c);
}
TEST(CompressString, All) {
std::string original(1000, 'A');
auto c = Compress(MakeCompressor("gzip").get(), original);
EXPECT_TRUE(c);
auto d = Decompress(MakeDecompressor("gzip").get(), *c);
EXPECT_TRUE(d);
EXPECT_EQ(FlattenSlow(*d), original);
TEST(CompressString, All)
{
std::string original(1000, 'A');
auto c = Compress(MakeCompressor("gzip").get(), original);
EXPECT_TRUE(c);
auto d = Decompress(MakeDecompressor("gzip").get(), *c);
EXPECT_TRUE(d);
EXPECT_EQ(FlattenSlow(*d), original);
}
TEST(CompressNoncontiguousBuffer, All) {
NoncontiguousBufferBuilder nbb;
std::string original(1000, 'A');
nbb.Append(original.data(), original.size());
auto &&nb = nbb.DestructiveGet();
auto c = Compress(MakeCompressor("gzip").get(), nb);
EXPECT_TRUE(c);
auto d = Decompress(MakeDecompressor("gzip").get(), *c);
EXPECT_TRUE(d);
EXPECT_EQ(FlattenSlow(*d), original);
TEST(CompressNoncontiguousBuffer, All)
{
NoncontiguousBufferBuilder nbb;
std::string original(1000, 'A');
nbb.Append(original.data(), original.size());
auto &&nb = nbb.DestructiveGet();
auto c = Compress(MakeCompressor("gzip").get(), nb);
EXPECT_TRUE(c);
auto d = Decompress(MakeDecompressor("gzip").get(), *c);
EXPECT_TRUE(d);
EXPECT_EQ(FlattenSlow(*d), original);
}
TEST(Decompressor, Empty) {
for (auto &&algo : algos) {
auto res = Decompress(MakeDecompressor(algo).get(), "");
TEST(Decompressor, Empty)
{
for (auto &&algo : algos) {
auto res = Decompress(MakeDecompressor(algo).get(), "");
// Failure in decompressing empty buffer is not an error. So long as
// decompressing a buffer produced by `Compress(..., "")` works correctly,
// we're fine.
if (res) {
// However, if the decompression does succeed, the resulting buffer should
// be empty.
EXPECT_TRUE(res->Empty());
// Failure in decompressing empty buffer is not an error. So long as
// decompressing a buffer produced by `Compress(..., "")` works correctly,
// we're fine.
if (res) {
// However, if the decompression does succeed, the resulting buffer should
// be empty.
EXPECT_TRUE(res->Empty());
}
}
}
}
TEST(Decompressor, Invalid) {
for (auto &&algo : algos) {
auto res =
Decompress(MakeDecompressor(algo).get(),
"this buffer is likely an invalid compressed buffer.");
EXPECT_FALSE(res);
}
TEST(Decompressor, Invalid)
{
for (auto &&algo : algos) {
auto res = Decompress(MakeDecompressor(algo).get(), "this buffer is likely an invalid compressed buffer.");
EXPECT_FALSE(res);
}
}
} // namespace tile
}// namespace tile

View File

@ -6,12 +6,12 @@
namespace tile {
class Configurable {
Configurable();
virtual ~Configurable() = default;
virtual void SetProperty(const Slice &name, const Slice &value) = 0;
virtual std::string GetProperty(const Slice &name) const = 0;
Configurable();
virtual ~Configurable() = default;
virtual void SetProperty(const Slice &name, const Slice &value) = 0;
virtual std::string GetProperty(const Slice &name) const = 0;
};
} // namespace tile
}// namespace tile
#endif // TILE_BASE_CONFIG_CONFIGURABLE_H
#endif// TILE_BASE_CONFIG_CONFIGURABLE_H

View File

@ -4,80 +4,99 @@
#include "tile/base/thread/unique_lock.h"
namespace tile {
bool Configuration::Has(const Slice &key) const {
UniqueLock<tile::Mutex> lock(mutex_);
std::string value;
return GetRaw(key, &value);
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) {
UniqueLock<tile::Mutex> lock(mutex_);
if (events_enabled_) {
OnRemoving(key);
}
RemoveRaw(key);
if (events_enabled_) {
OnRemoved(key);
}
void
Configuration::Remove(const Slice &key)
{
UniqueLock<tile::Mutex> lock(mutex_);
if (events_enabled_) { OnRemoving(key); }
RemoveRaw(key);
if (events_enabled_) { OnRemoved(key); }
}
void Configuration::EnableEvents(bool enable) { events_enabled_ = enable; }
bool Configuration::EventsEnabled() const { return events_enabled_; }
Configuration::Keys Configuration::keys(const Slice &root) const {
UniqueLock<tile::Mutex> lock(mutex_);
return Enumerate(root);
void
Configuration::EnableEvents(bool enable)
{
events_enabled_ = enable;
}
#define TILE_DEFINE_CONFIG_GETTER(type, name) \
std::optional<type> Configuration::Get##name(const Slice &key) const { \
std::string value; \
if (GetRaw(key, &value)) { \
auto opt = TryParseTraits<type>::TryParse(value); \
if (opt.has_value()) { \
return *opt; \
} else { \
return std::nullopt; \
} \
} else { \
return std::nullopt; \
} \
} \
type Configuration::Get##name(const Slice &key, type default_value) const { \
auto opt = Get##name(key); \
if (opt.has_value()) { \
return *opt; \
} else { \
return default_value; \
} \
}
#define TILE_DEFINE_CONFIG_SETTER(type, name) \
void Configuration::Set##name(const Slice &key, type value) { \
SetRawWithEvent(key, Format("{}", value)); \
}
std::optional<std::string> Configuration::GetString(const Slice &key) const {
std::string value;
UniqueLock<tile::Mutex> lock(mutex_);
if (GetRaw(key, &value)) {
return value;
} else {
return std::nullopt;
}
bool
Configuration::EventsEnabled() const
{
return events_enabled_;
}
std::string Configuration::GetString(const Slice &key,
std::string default_value) const {
auto opt = GetString(key);
return opt.has_value() ? *opt : default_value;
Configuration::Keys
Configuration::keys(const Slice &root) const
{
UniqueLock<tile::Mutex> lock(mutex_);
return Enumerate(root);
}
void Configuration::SetString(const Slice &key, const std::string &value) {
SetRawWithEvent(key, value);
#define TILE_DEFINE_CONFIG_GETTER(type, name) \
std::optional<type> Configuration::Get##name(const Slice &key) const \
{ \
std::string value; \
if (GetRaw(key, &value)) { \
auto opt = TryParseTraits<type>::TryParse(value); \
if (opt.has_value()) { \
return *opt; \
} else { \
return std::nullopt; \
} \
} else { \
return std::nullopt; \
} \
} \
type Configuration::Get##name(const Slice &key, type default_value) const \
{ \
auto opt = Get##name(key); \
if (opt.has_value()) { \
return *opt; \
} else { \
return default_value; \
} \
}
#define TILE_DEFINE_CONFIG_SETTER(type, name) \
void Configuration::Set##name(const Slice &key, type value) { SetRawWithEvent(key, Format("{}", value)); }
std::optional<std::string>
Configuration::GetString(const Slice &key) const
{
std::string value;
UniqueLock<tile::Mutex> lock(mutex_);
if (GetRaw(key, &value)) {
return value;
} else {
return std::nullopt;
}
}
void Configuration::SetBool(const Slice &key, bool value) {
SetRawWithEvent(key, value ? "true" : "false");
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)
{
SetRawWithEvent(key, value);
}
void
Configuration::SetBool(const Slice &key, bool value)
{
SetRawWithEvent(key, value ? "true" : "false");
}
TILE_DEFINE_CONFIG_GETTER(int, Int)
@ -101,22 +120,19 @@ 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);
}
{
UniqueLock<tile::Mutex> lock(mutex_);
SetRaw(key, value);
}
if (events_enabled_) {
OnChanged(key, value);
}
if (events_enabled_) { OnChanged(key, value); }
}
Configuration::~Configuration() {}
} // namespace tile
}// namespace tile

View File

@ -12,113 +12,111 @@
namespace tile {
class Configuration : public RefCounted<Configuration> {
public:
using Keys = std::vector<std::string>;
using Ptr = RefPtr<Configuration>;
using Keys = std::vector<std::string>;
using Ptr = RefPtr<Configuration>;
// events
// Key, Value
sigslot::signal2<const Slice &, const Slice &> OnChanging;
// Key, Value
sigslot::signal2<const Slice &, const Slice &> OnChanged;
// Key
sigslot::signal1<const Slice &> OnRemoving;
// Key
sigslot::signal1<const Slice &> OnRemoved;
// events
// Key, Value
sigslot::signal2<const Slice &, const Slice &> OnChanging;
// Key, Value
sigslot::signal2<const Slice &, const Slice &> OnChanged;
// Key
sigslot::signal1<const Slice &> OnRemoving;
// Key
sigslot::signal1<const Slice &> OnRemoved;
bool Has(const Slice &key) const;
void Remove(const Slice &key);
void EnableEvents(bool enable = true);
bool EventsEnabled() const;
bool Has(const Slice &key) const;
void Remove(const Slice &key);
void EnableEvents(bool enable = true);
bool EventsEnabled() const;
Keys keys(const Slice &root = "") const;
Keys keys(const Slice &root = "") const;
#define TILE_DECLARE_CONFIG_GETTER(type, name) \
std::optional<type> Get##name(const Slice &key) const; \
type Get##name(const Slice &key, type default_value) const;
#define TILE_DECLARE_CONFIG_GETTER(type, name) \
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()
// int GetInt()
// unsigned int GetUInt()
// int16_t GetInt16()
// int32_t GetInt32()
// int64_t GetInt64()
// uint16_t GetUInt16()
// uint32_t GetUInt32()
// uint64_t GetUInt64()
// double GetDouble()
// bool GetBool()
std::string GetRawString(const Slice &key, const Slice &default_value) const;
// std::stirng GetString()
// int GetInt()
// unsigned int GetUInt()
// int16_t GetInt16()
// int32_t GetInt32()
// int64_t GetInt64()
// uint16_t GetUInt16()
// uint32_t GetUInt32()
// uint64_t GetUInt64()
// double GetDouble()
// bool GetBool()
// getters
TILE_DECLARE_CONFIG_GETTER(std::string, String)
TILE_DECLARE_CONFIG_GETTER(int, Int)
TILE_DECLARE_CONFIG_GETTER(unsigned int, UInt)
TILE_DECLARE_CONFIG_GETTER(int16_t, Int16)
TILE_DECLARE_CONFIG_GETTER(int32_t, Int32)
TILE_DECLARE_CONFIG_GETTER(int64_t, Int64)
TILE_DECLARE_CONFIG_GETTER(uint16_t, UInt16)
TILE_DECLARE_CONFIG_GETTER(uint32_t, UInt32)
TILE_DECLARE_CONFIG_GETTER(uint64_t, UInt64)
TILE_DECLARE_CONFIG_GETTER(double, Double)
TILE_DECLARE_CONFIG_GETTER(bool, Bool)
// getters
TILE_DECLARE_CONFIG_GETTER(std::string, String)
TILE_DECLARE_CONFIG_GETTER(int, Int)
TILE_DECLARE_CONFIG_GETTER(unsigned int, UInt)
TILE_DECLARE_CONFIG_GETTER(int16_t, Int16)
TILE_DECLARE_CONFIG_GETTER(int32_t, Int32)
TILE_DECLARE_CONFIG_GETTER(int64_t, Int64)
TILE_DECLARE_CONFIG_GETTER(uint16_t, UInt16)
TILE_DECLARE_CONFIG_GETTER(uint32_t, UInt32)
TILE_DECLARE_CONFIG_GETTER(uint64_t, UInt64)
TILE_DECLARE_CONFIG_GETTER(double, Double)
TILE_DECLARE_CONFIG_GETTER(bool, Bool)
protected:
// setters
// SetString(const Slice &key, const std::string &value)
// SetInt(const Slice &key, int value)
// SetUInt(const Slice &key, unsigned int value)
// SetInt16(const Slice &key, int16_t value)
// SetInt32(const Slice &key, int32_t value)
// SetInt64(const Slice &key, int64_t value)
// SetUInt16(const Slice &key, uint16_t value)
// SetUInt32(const Slice &key, uint32_t value)
// SetUInt64(const Slice &key, uint64_t value)
// SetDouble(const Slice &key, double value)
// SetBool(const Slice &key, bool value)
TILE_DECLARE_CONFIG_SETTER(const std::string &, String)
TILE_DECLARE_CONFIG_SETTER(int, Int)
TILE_DECLARE_CONFIG_SETTER(unsigned int, UInt)
TILE_DECLARE_CONFIG_SETTER(int16_t, Int16)
TILE_DECLARE_CONFIG_SETTER(int32_t, Int32)
TILE_DECLARE_CONFIG_SETTER(int64_t, Int64)
TILE_DECLARE_CONFIG_SETTER(uint16_t, UInt16)
TILE_DECLARE_CONFIG_SETTER(uint32_t, UInt32)
TILE_DECLARE_CONFIG_SETTER(uint64_t, UInt64)
TILE_DECLARE_CONFIG_SETTER(double, Double)
TILE_DECLARE_CONFIG_SETTER(bool, Bool)
// setters
// SetString(const Slice &key, const std::string &value)
// SetInt(const Slice &key, int value)
// SetUInt(const Slice &key, unsigned int value)
// SetInt16(const Slice &key, int16_t value)
// SetInt32(const Slice &key, int32_t value)
// SetInt64(const Slice &key, int64_t value)
// SetUInt16(const Slice &key, uint16_t value)
// SetUInt32(const Slice &key, uint32_t value)
// SetUInt64(const Slice &key, uint64_t value)
// SetDouble(const Slice &key, double value)
// SetBool(const Slice &key, bool value)
TILE_DECLARE_CONFIG_SETTER(const std::string &, String)
TILE_DECLARE_CONFIG_SETTER(int, Int)
TILE_DECLARE_CONFIG_SETTER(unsigned int, UInt)
TILE_DECLARE_CONFIG_SETTER(int16_t, Int16)
TILE_DECLARE_CONFIG_SETTER(int32_t, Int32)
TILE_DECLARE_CONFIG_SETTER(int64_t, Int64)
TILE_DECLARE_CONFIG_SETTER(uint16_t, UInt16)
TILE_DECLARE_CONFIG_SETTER(uint32_t, UInt32)
TILE_DECLARE_CONFIG_SETTER(uint64_t, UInt64)
TILE_DECLARE_CONFIG_SETTER(double, Double)
TILE_DECLARE_CONFIG_SETTER(bool, Bool)
#undef TILE_DECLARE_CONFIG_GETTER
#undef TILE_DECLARE_CONFIG_SETTER
protected:
class ScopedLock {
public:
explicit ScopedLock(const Configuration &config) : config_(config) {
config_.mutex_.Lock();
}
~ScopedLock() { config_.mutex_.Unlock(); }
class ScopedLock {
public:
explicit ScopedLock(const Configuration &config) : config_(config) { config_.mutex_.Lock(); }
private:
const Configuration &config_;
};
~ScopedLock() { config_.mutex_.Unlock(); }
virtual bool GetRaw(const Slice &key, std::string *value) const = 0;
virtual bool SetRaw(const Slice &key, const Slice &value) = 0;
virtual void RemoveRaw(const Slice &key) = 0;
virtual Keys Enumerate(const Slice &range) const = 0;
void SetRawWithEvent(const Slice &key, const std::string &value);
virtual ~Configuration();
private:
const Configuration &config_;
};
friend class std::default_delete<Configuration>;
friend class LayeredConfiguration;
virtual bool GetRaw(const Slice &key, std::string *value) const = 0;
virtual bool SetRaw(const Slice &key, const Slice &value) = 0;
virtual void RemoveRaw(const Slice &key) = 0;
virtual Keys Enumerate(const Slice &range) const = 0;
void SetRawWithEvent(const Slice &key, const std::string &value);
virtual ~Configuration();
friend class std::default_delete<Configuration>;
friend class LayeredConfiguration;
private:
mutable Mutex mutex_;
bool events_enabled_{false};
mutable Mutex mutex_;
bool events_enabled_{false};
};
} // namespace tile
}// namespace tile
#endif // TILE_BASE_CONFIG_CONFIGURATION_H
#endif// TILE_BASE_CONFIG_CONFIGURATION_H

View File

@ -1,143 +1,152 @@
#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);
}
return true;
}
bool IniFileConfiguration::load(const std::string &path) {
std::ifstream istr(path);
return load(istr);
}
bool IniFileConfiguration::GetRaw(const Slice &key, std::string *value) const {
auto iter = map_.find(key.ToString());
if (iter != map_.end()) {
*value = iter->second;
Configuration::ScopedLock lock(*this);
map_.clear();
section_key_.clear();
while (!istr.eof()) { ParseLine(istr); }
return true;
} else {
return false;
}
}
bool IniFileConfiguration::SetRaw(const Slice &key, const Slice &value) {
map_[key] = value;
return true;
}
void IniFileConfiguration::RemoveRaw(const Slice &key) {
std::string prefix = key;
if (!prefix.empty()) {
prefix.push_back('.');
}
IStringMap::iterator it = map_.begin();
IStringMap::iterator cur;
while (it != map_.end()) {
if (EqualsIgnoreCase(cur->first, key) ||
EqualsIgnoreCase(cur->first, prefix)) {
it = map_.erase(cur);
bool
IniFileConfiguration::load(const std::string &path)
{
std::ifstream istr(path);
return load(istr);
}
bool
IniFileConfiguration::GetRaw(const Slice &key, std::string *value) const
{
auto iter = map_.find(key.ToString());
if (iter != map_.end()) {
*value = iter->second;
return true;
} else {
++it;
return false;
}
}
}
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('.');
}
std::string::size_type psize = prefix.size();
for (const auto &p : map_) {
auto &key = p.first;
if (EqualsIgnoreCase(key, prefix, psize)) {
std::string::size_type end = key.find('.', psize);
std::string subkey;
if (end == std::string::npos) {
subkey = key.substr(psize);
} else {
subkey = key.substr(psize, end - psize);
}
if (keys.find(subkey) == keys.end()) {
keys.insert(subkey);
range.push_back(subkey);
}
}
}
return range;
bool
IniFileConfiguration::SetRaw(const Slice &key, const Slice &value)
{
map_[key] = value;
return true;
}
void IniFileConfiguration::ParseLine(std::istream &istr) {
static const int eof = std::char_traits<char>::eof();
auto ReadLine = [&](std::string *line) {
line->clear();
while (true) {
int c = istr.get();
if (c == eof || c == '\n') {
return c != eof;
} else {
*line += (char)c;
}
void
IniFileConfiguration::RemoveRaw(const Slice &key)
{
std::string prefix = key;
if (!prefix.empty()) { prefix.push_back('.'); }
IStringMap::iterator it = map_.begin();
IStringMap::iterator cur;
while (it != map_.end()) {
if (EqualsIgnoreCase(cur->first, key) || EqualsIgnoreCase(cur->first, prefix)) {
it = map_.erase(cur);
} else {
++it;
}
}
};
std::string raw_line;
while (ReadLine(&raw_line)) {
if (raw_line.empty()) {
continue;
}
Slice line = TrimLeft(raw_line);
if (line.empty() || line[0] == ';' || line[0] == '#') {
// skip empty line
// skip comment line ; or #
continue;
}
// parse section
if (line[0] == '[') {
auto pos = line.find(']');
if (pos == Slice::npos) {
section_key_ = Trim(line.substr(1));
} else {
section_key_ = Trim(line.substr(1, pos - 1));
}
} else {
auto strs = Split(line, "=", true, 2);
std::string full_key = section_key_;
if (!full_key.empty()) {
full_key.push_back('.');
}
full_key.append(Trim(strs[0]));
if (strs.size() > 1) {
map_[full_key] = Trim(strs[1]);
} else {
map_[full_key] = "";
}
}
}
}
bool 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;
}
} // namespace tile
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('.'); }
std::string::size_type psize = prefix.size();
for (const auto &p : map_) {
auto &key = p.first;
if (EqualsIgnoreCase(key, prefix, psize)) {
std::string::size_type end = key.find('.', psize);
std::string subkey;
if (end == std::string::npos) {
subkey = key.substr(psize);
} else {
subkey = key.substr(psize, end - psize);
}
if (keys.find(subkey) == keys.end()) {
keys.insert(subkey);
range.push_back(subkey);
}
}
}
return range;
}
void
IniFileConfiguration::ParseLine(std::istream &istr)
{
static const int eof = std::char_traits<char>::eof();
auto ReadLine = [&](std::string *line) {
line->clear();
while (true) {
int c = istr.get();
if (c == eof || c == '\n') {
return c != eof;
} else {
*line += (char) c;
}
}
};
std::string raw_line;
while (ReadLine(&raw_line)) {
if (raw_line.empty()) { continue; }
Slice line = TrimLeft(raw_line);
if (line.empty() || line[0] == ';' || line[0] == '#') {
// skip empty line
// skip comment line ; or #
continue;
}
// parse section
if (line[0] == '[') {
auto pos = line.find(']');
if (pos == Slice::npos) {
section_key_ = Trim(line.substr(1));
} else {
section_key_ = Trim(line.substr(1, pos - 1));
}
} else {
auto strs = Split(line, "=", true, 2);
std::string full_key = section_key_;
if (!full_key.empty()) { full_key.push_back('.'); }
full_key.append(Trim(strs[0]));
if (strs.size() > 1) {
map_[full_key] = Trim(strs[1]);
} else {
map_[full_key] = "";
}
}
}
}
bool
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;
}
}// namespace tile

View File

@ -9,30 +9,32 @@
namespace tile {
class IniFileConfiguration : public Configuration {
public:
using Ptr = RefPtr<IniFileConfiguration>;
using Ptr = RefPtr<IniFileConfiguration>;
IniFileConfiguration();
~IniFileConfiguration() override;
// IniFileConfig(std::istream &istr);
// IniFileConfig(const std::string &path);
bool load(std::istream &istr);
bool load(const std::string &path);
IniFileConfiguration();
~IniFileConfiguration() override;
// IniFileConfig(std::istream &istr);
// IniFileConfig(const std::string &path);
bool load(std::istream &istr);
bool load(const std::string &path);
protected:
bool GetRaw(const Slice &key, std::string *value) const override;
bool SetRaw(const Slice &key, const Slice &value) override;
void RemoveRaw(const Slice &key) override;
Keys Enumerate(const Slice &range) const override;
bool GetRaw(const Slice &key, std::string *value) const override;
bool SetRaw(const Slice &key, const Slice &value) override;
void RemoveRaw(const Slice &key) override;
Keys Enumerate(const Slice &range) const override;
private:
void ParseLine(std::istream &istr);
struct ICompare {
bool operator()(const std::string &s1, const std::string &s2) const;
};
typedef std::map<std::string, std::string, ICompare> IStringMap;
IStringMap map_;
std::string section_key_;
};
} // namespace tile
void ParseLine(std::istream &istr);
#endif // TILE_BASE_CONFIG_INI_FILE_CONFIG_H
struct ICompare {
bool operator()(const std::string &s1, const std::string &s2) const;
};
typedef std::map<std::string, std::string, ICompare> IStringMap;
IStringMap map_;
std::string section_key_;
};
}// namespace tile
#endif// TILE_BASE_CONFIG_INI_FILE_CONFIG_H

View File

@ -17,29 +17,29 @@ a=4
namespace tile {
static_assert(
!detail::HasClassofImpl<IniFileConfiguration, Configuration>::value, "");
static_assert(!detail::HasClassofImpl<IniFileConfiguration, Configuration>::value, "");
TEST(IniFileConfig, LoadFromIStream) {
std::stringstream ss(kIniFileConfig);
Configuration::Ptr config = MakeRefCounted<IniFileConfiguration>();
ASSERT_FALSE(config->Has("a"));
ASSERT_FALSE(config->Has("sec1.a"));
ASSERT_FALSE(config->Has("sec3.a"));
if (config.Is<IniFileConfiguration>()) {
IniFileConfiguration::Ptr ini = config.As<IniFileConfiguration>();
ASSERT_TRUE(ini->load(ss));
}
ASSERT_TRUE(config->Has("a"));
ASSERT_TRUE(config->Has("sec1.a"));
ASSERT_TRUE(config->Has("sec3.a"));
ASSERT_TRUE(config->Has("sec2.kk.a"));
TEST(IniFileConfig, LoadFromIStream)
{
std::stringstream ss(kIniFileConfig);
Configuration::Ptr config = MakeRefCounted<IniFileConfiguration>();
ASSERT_FALSE(config->Has("a"));
ASSERT_FALSE(config->Has("sec1.a"));
ASSERT_FALSE(config->Has("sec3.a"));
if (config.Is<IniFileConfiguration>()) {
IniFileConfiguration::Ptr ini = config.As<IniFileConfiguration>();
ASSERT_TRUE(ini->load(ss));
}
ASSERT_TRUE(config->Has("a"));
ASSERT_TRUE(config->Has("sec1.a"));
ASSERT_TRUE(config->Has("sec3.a"));
ASSERT_TRUE(config->Has("sec2.kk.a"));
ASSERT_TRUE(config->GetInt("a"));
ASSERT_TRUE(config->GetInt("sec1.a"));
ASSERT_EQ(1, *config->GetInt("a"));
ASSERT_EQ(2, *config->GetInt("sec1.a"));
ASSERT_EQ(3, *config->GetInt("sec3.a"));
ASSERT_EQ(4, *config->GetInt("sec2.kk.a"));
ASSERT_TRUE(config->GetInt("a"));
ASSERT_TRUE(config->GetInt("sec1.a"));
ASSERT_EQ(1, *config->GetInt("a"));
ASSERT_EQ(2, *config->GetInt("sec1.a"));
ASSERT_EQ(3, *config->GetInt("sec3.a"));
ASSERT_EQ(4, *config->GetInt("sec2.kk.a"));
}
} // namespace tile
}// namespace tile

View File

@ -1,141 +1,153 @@
#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) {
std::ifstream istr(path);
return load(istr);
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();
Json::Reader reader;
return reader.parse(istr, object_, true);
Configuration::ScopedLock _(*this);
object_.clear();
Json::Reader reader;
return reader.parse(istr, object_, true);
}
void JSONConfiguration::SetInt(const Slice &key, int value) {
Configuration::ScopedLock _(*this);
SetValue(key, value);
void
JSONConfiguration::SetInt(const Slice &key, int value)
{
Configuration::ScopedLock _(*this);
SetValue(key, value);
}
void JSONConfiguration::SetBool(const Slice &key, bool value) {
Configuration::ScopedLock _(*this);
SetValue(key, value);
void
JSONConfiguration::SetBool(const Slice &key, bool value)
{
Configuration::ScopedLock _(*this);
SetValue(key, value);
}
void JSONConfiguration::SetDouble(const Slice &key, double value) {
Configuration::ScopedLock _(*this);
SetValue(key, 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) {
Configuration::ScopedLock _(*this);
SetValue(key, value);
void
JSONConfiguration::SetString(const Slice &key, const std::string &value)
{
Configuration::ScopedLock _(*this);
SetValue(key, value);
}
void JSONConfiguration::RemoveRaw(const Slice &key) {
Json::Value *root;
Slice last_part;
if (!FindStart(key, &last_part, &root)) {
return;
}
root->removeMember(last_part);
}
std::string JSONConfiguration::Dump() const {
Configuration::ScopedLock _(*this);
return object_.toStyledString();
void
JSONConfiguration::RemoveRaw(const Slice &key)
{
Json::Value *root;
Slice last_part;
if (!FindStart(key, &last_part, &root)) { return; }
root->removeMember(last_part);
}
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) {
const auto &cur_key = keys[i];
if (cur_key.empty()) {
TILE_LOG_ERROR("Invalid key: {}", key);
return false;
}
if (!root.isMember(cur_key)) {
return false;
}
root = root[cur_key];
}
if (root.isConvertibleTo(Json::stringValue)) {
*value = root.asString();
} else {
*value = root.toStyledString();
}
return true;
std::string
JSONConfiguration::Dump() const
{
Configuration::ScopedLock _(*this);
return object_.toStyledString();
}
bool JSONConfiguration::SetRaw(const Slice &key, const Slice &value) {
return SetValue(key, value.ToString());
}
Configuration::Keys JSONConfiguration::Enumerate(const Slice &range) const {
Configuration::Keys key_set;
std::string prefix = range;
if (!prefix.empty()) {
prefix += ".";
}
auto keys = Split(range, '.');
auto root = object_;
for (std::size_t i = 0; i < keys.size(); ++i) {
const auto &cur_key = keys[i];
if (cur_key.empty()) {
TILE_LOG_ERROR("Invalid range: {}", range);
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);
}
return key_set;
}
bool JSONConfiguration::FindStart(const Slice &key, Slice *last_part,
Json::Value **parent_obj) {
auto keys = Split(key, '.');
if (keys.empty()) {
return false;
}
Json::Value *root = &object_;
for (std::size_t i = 0; i < keys.size() - 1; ++i) {
const auto &cur_key = keys[i];
if (cur_key.empty()) {
TILE_LOG_ERROR("Invalid key: {}", key);
return false;
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) {
const auto &cur_key = keys[i];
if (cur_key.empty()) {
TILE_LOG_ERROR("Invalid key: {}", key);
return false;
}
if (!root.isMember(cur_key)) { return false; }
root = root[cur_key];
}
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);
return false;
if (root.isConvertibleTo(Json::stringValue)) {
*value = root.asString();
} else {
*value = root.toStyledString();
}
root = &(*root)[cur_key];
}
*last_part = keys.back();
*parent_obj = root;
return true;
return true;
}
} // namespace tile
bool
JSONConfiguration::SetRaw(const Slice &key, const Slice &value)
{
return SetValue(key, value.ToString());
}
Configuration::Keys
JSONConfiguration::Enumerate(const Slice &range) const
{
Configuration::Keys key_set;
std::string prefix = range;
if (!prefix.empty()) { prefix += "."; }
auto keys = Split(range, '.');
auto root = object_;
for (std::size_t i = 0; i < keys.size(); ++i) {
const auto &cur_key = keys[i];
if (cur_key.empty()) {
TILE_LOG_ERROR("Invalid range: {}", range);
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); }
return key_set;
}
bool
JSONConfiguration::FindStart(const Slice &key, Slice *last_part, Json::Value **parent_obj)
{
auto keys = Split(key, '.');
if (keys.empty()) { return false; }
Json::Value *root = &object_;
for (std::size_t i = 0; i < keys.size() - 1; ++i) {
const auto &cur_key = keys[i];
if (cur_key.empty()) {
TILE_LOG_ERROR("Invalid key: {}", key);
return false;
}
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);
return false;
}
root = &(*root)[cur_key];
}
*last_part = keys.back();
*parent_obj = root;
return true;
}
}// namespace tile

View File

@ -9,41 +9,42 @@
namespace tile {
class JSONConfiguration : public Configuration {
public:
using Ptr = RefPtr<JSONConfiguration>;
JSONConfiguration();
~JSONConfiguration() override;
bool load(const std::string &path);
bool load(std::istream &istr);
using Ptr = RefPtr<JSONConfiguration>;
JSONConfiguration();
~JSONConfiguration() override;
bool load(const std::string &path);
bool load(std::istream &istr);
void SetInt(const Slice &key, int value) override;
void SetBool(const Slice &key, bool value) override;
void SetDouble(const Slice &key, double value) override;
void SetString(const Slice &key, const std::string &value) override;
void RemoveRaw(const Slice &key) override;
void SetInt(const Slice &key, int value) override;
void SetBool(const Slice &key, bool value) override;
void SetDouble(const Slice &key, double value) override;
void SetString(const Slice &key, const std::string &value) override;
void RemoveRaw(const Slice &key) override;
std::string Dump() const;
std::string Dump() const;
protected:
bool GetRaw(const Slice &key, std::string *value) const override;
bool SetRaw(const Slice &key, const Slice &value) override;
Keys Enumerate(const Slice &range) const override;
bool GetRaw(const Slice &key, std::string *value) const override;
bool SetRaw(const Slice &key, const Slice &value) override;
Keys Enumerate(const Slice &range) const override;
private:
bool FindStart(const Slice &key, Slice *last_prt, Json::Value **parent_obj);
template <typename T> bool SetValue(const Slice &key, T value);
Json::Value object_;
bool FindStart(const Slice &key, Slice *last_prt, Json::Value **parent_obj);
template<typename T>
bool SetValue(const Slice &key, T value);
Json::Value object_;
};
template <typename T>
bool JSONConfiguration::SetValue(const Slice &key, T value) {
Slice last_part;
Json::Value *root;
if (!FindStart(key, &last_part, &root)) {
return false;
}
(*root)[last_part] = Json::Value(value);
return true;
template<typename T>
bool
JSONConfiguration::SetValue(const Slice &key, T value)
{
Slice last_part;
Json::Value *root;
if (!FindStart(key, &last_part, &root)) { return false; }
(*root)[last_part] = Json::Value(value);
return true;
}
} // namespace tile
}// namespace tile
#endif // TILE_BASE_CONFIG_JSON_CONFIGURATION_H
#endif// TILE_BASE_CONFIG_JSON_CONFIGURATION_H

View File

@ -18,81 +18,87 @@ const char *kJsonConfig = R"(
)";
}
TEST(JSONConfiguration, Load) {
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
}
TEST(JSONConfiguration, Has) {
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
ASSERT_TRUE(config.Has("key1"));
ASSERT_TRUE(config.Has("key2"));
ASSERT_TRUE(config.Has("key3"));
ASSERT_TRUE(config.Has("key4"));
ASSERT_TRUE(config.Has("key5"));
ASSERT_TRUE(config.Has("key5.key51"));
ASSERT_TRUE(config.Has("key5.key52"));
TEST(JSONConfiguration, Load)
{
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
}
TEST(JSONConfiguration, SampleSet) {
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
ASSERT_TRUE(config.GetInt32("key2"));
ASSERT_EQ(*config.GetInt32("key2"), 2);
TEST(JSONConfiguration, Has)
{
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
config.SetInt("key2", 20);
ASSERT_TRUE(config.GetInt32("key2"));
ASSERT_EQ(20, *config.GetInt32("key2")) << config.Dump();
config.SetDouble("key3", 30.0);
ASSERT_TRUE(config.GetDouble("key3"));
ASSERT_NEAR(30.0, *config.GetDouble("key3"), 0.0001) << config.Dump();
config.SetBool("key4", false);
ASSERT_TRUE(config.GetBool("key4"));
ASSERT_FALSE(*config.GetBool("key4")) << config.Dump();
ASSERT_TRUE(config.Has("key1"));
ASSERT_TRUE(config.Has("key2"));
ASSERT_TRUE(config.Has("key3"));
ASSERT_TRUE(config.Has("key4"));
ASSERT_TRUE(config.Has("key5"));
ASSERT_TRUE(config.Has("key5.key51"));
ASSERT_TRUE(config.Has("key5.key52"));
}
TEST(JSONConfiguration, LayeredSet) {
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
TEST(JSONConfiguration, SampleSet)
{
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
ASSERT_TRUE(config.GetInt32("key2"));
ASSERT_EQ(*config.GetInt32("key2"), 2);
// ASSERT_TRUE(config.GetInt32("key5.key52"));
config.SetInt("key5.key52", 520);
ASSERT_TRUE(config.GetInt32("key5.key52")) << config.Dump();
ASSERT_EQ(520, *config.GetInt32("key5.key52")) << config.Dump();
config.SetInt("key2", 20);
ASSERT_TRUE(config.GetInt32("key2"));
ASSERT_EQ(20, *config.GetInt32("key2")) << config.Dump();
// override by other type
config.SetBool("key5.key52", false);
ASSERT_TRUE(config.GetBool("key5.key52")) << config.Dump();
ASSERT_FALSE(*config.GetBool("key5.key52")) << config.Dump();
config.SetDouble("key3", 30.0);
ASSERT_TRUE(config.GetDouble("key3"));
ASSERT_NEAR(30.0, *config.GetDouble("key3"), 0.0001) << config.Dump();
config.SetBool("key4", false);
ASSERT_TRUE(config.GetBool("key4"));
ASSERT_FALSE(*config.GetBool("key4")) << config.Dump();
}
TEST(json_configuration, Enumerate) {
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
auto keys = config.keys("");
ASSERT_EQ(5, keys.size());
ASSERT_EQ("key1", keys[0]);
ASSERT_EQ("key2", keys[1]);
ASSERT_EQ("key3", keys[2]);
ASSERT_EQ("key4", keys[3]);
ASSERT_EQ("key5", keys[4]);
TEST(JSONConfiguration, LayeredSet)
{
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
keys = config.keys("key5");
ASSERT_EQ(2, keys.size());
ASSERT_EQ("key5.key51", keys[0]);
ASSERT_EQ("key5.key52", keys[1]);
// ASSERT_TRUE(config.GetInt32("key5.key52"));
config.SetInt("key5.key52", 520);
ASSERT_TRUE(config.GetInt32("key5.key52")) << config.Dump();
ASSERT_EQ(520, *config.GetInt32("key5.key52")) << config.Dump();
config.Remove("key5.key51");
keys = config.keys("key5");
ASSERT_EQ("key5.key52", keys[0]);
// override by other type
config.SetBool("key5.key52", false);
ASSERT_TRUE(config.GetBool("key5.key52")) << config.Dump();
ASSERT_FALSE(*config.GetBool("key5.key52")) << config.Dump();
}
} // namespace tile
TEST(json_configuration, Enumerate)
{
JSONConfiguration config;
std::istringstream istr(kJsonConfig);
ASSERT_TRUE(config.load(istr));
auto keys = config.keys("");
ASSERT_EQ(5, keys.size());
ASSERT_EQ("key1", keys[0]);
ASSERT_EQ("key2", keys[1]);
ASSERT_EQ("key3", keys[2]);
ASSERT_EQ("key4", keys[3]);
ASSERT_EQ("key5", keys[4]);
keys = config.keys("key5");
ASSERT_EQ(2, keys.size());
ASSERT_EQ("key5.key51", keys[0]);
ASSERT_EQ("key5.key52", keys[1]);
config.Remove("key5.key51");
keys = config.keys("key5");
ASSERT_EQ("key5.key52", keys[0]);
}
}// namespace tile

View File

@ -3,129 +3,147 @@
namespace tile {
LayeredConfiguration::LayeredConfiguration() {}
LayeredConfiguration::~LayeredConfiguration() {}
void LayeredConfiguration::Add(Configuration::Ptr cfg) {
Add(cfg, highest(), false);
void
LayeredConfiguration::Add(Configuration::Ptr cfg)
{
Add(cfg, highest(), false);
}
void LayeredConfiguration::Add(Configuration::Ptr cfg, int priority) {
Add(cfg, priority, false);
void
LayeredConfiguration::Add(Configuration::Ptr cfg, int priority)
{
Add(cfg, priority, false);
}
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)
{
Add(cfg, label, highest(), false);
}
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, int priority)
{
Add(cfg, label, priority, false);
}
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, bool writeable)
{
Add(cfg, label, highest(), writeable);
}
void LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label,
int priority, bool writeable) {
Configuration::ScopedLock lock(*this);
ConfigItem item;
item.cfg = cfg;
item.priority = priority;
item.writeable = writeable;
item.label = label;
auto it = configs_.begin();
while (it != configs_.end() && it->priority < priority) {
++it;
}
configs_.insert(it, item);
void
LayeredConfiguration::Add(Configuration::Ptr cfg, const std::string &label, int priority, bool writeable)
{
Configuration::ScopedLock lock(*this);
ConfigItem item;
item.cfg = cfg;
item.priority = priority;
item.writeable = writeable;
item.label = label;
auto it = configs_.begin();
while (it != configs_.end() && it->priority < priority) { ++it; }
configs_.insert(it, item);
}
void LayeredConfiguration::Add(Configuration::Ptr cfg, int priority,
bool writeable) {
Add(cfg, std::string(), priority, writeable);
}
void LayeredConfiguration::AddWriteable(Configuration::Ptr cfg, int priority) {
Add(cfg, priority, true);
void
LayeredConfiguration::Add(Configuration::Ptr cfg, int priority, bool writeable)
{
Add(cfg, std::string(), priority, writeable);
}
Configuration::Ptr LayeredConfiguration::Find(const Slice &label) const {
Configuration::ScopedLock lock(*this);
void
LayeredConfiguration::AddWriteable(Configuration::Ptr cfg, int priority)
{
Add(cfg, priority, true);
}
for (const auto &conf : configs_) {
if (conf.label == label) {
return conf.cfg;
Configuration::Ptr
LayeredConfiguration::Find(const Slice &label) const
{
Configuration::ScopedLock lock(*this);
for (const auto &conf : configs_) {
if (conf.label == label) { return conf.cfg; }
}
}
return 0;
return 0;
}
void LayeredConfiguration::RemoveConfig(Configuration::Ptr cfg) {
Configuration::ScopedLock lock(*this);
for (auto it = configs_.begin(); it != configs_.end();) {
if (it->cfg == cfg) {
it = configs_.erase(it);
void
LayeredConfiguration::RemoveConfig(Configuration::Ptr cfg)
{
Configuration::ScopedLock lock(*this);
for (auto it = configs_.begin(); it != configs_.end();) {
if (it->cfg == cfg) {
it = configs_.erase(it);
} else {
++it;
}
}
}
bool
LayeredConfiguration::GetRaw(const Slice &key, std::string *value) const
{
for (const auto &conf : configs_) {
if (conf.cfg->GetRaw(key, value)) { return true; }
}
return false;
}
bool
LayeredConfiguration::SetRaw(const Slice &key, const Slice &value)
{
for (const auto &conf : configs_) {
if (conf.writeable && conf.cfg->SetRaw(key, value)) { return true; }
}
return false;
}
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); }
}
}
return keys;
}
void
LayeredConfiguration::RemoveRaw(const std::string &key)
{
for (auto &conf : configs_) {
if (conf.writeable) { conf.cfg->RemoveRaw(key); }
}
}
int
LayeredConfiguration::lowest() const
{
if (configs_.empty()) {
return 0;
} else {
++it;
return configs_.front().priority - 1;
}
}
}
bool LayeredConfiguration::GetRaw(const Slice &key, std::string *value) const {
for (const auto &conf : configs_) {
if (conf.cfg->GetRaw(key, value)) {
return true;
int
LayeredConfiguration::highest() const
{
if (configs_.empty()) {
return 0;
} else {
return configs_.back().priority + 1;
}
}
return false;
}
bool LayeredConfiguration::SetRaw(const Slice &key, const Slice &value) {
for (const auto &conf : configs_) {
if (conf.writeable && conf.cfg->SetRaw(key, value)) {
return true;
}
}
return false;
}
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);
}
}
}
return keys;
}
void LayeredConfiguration::RemoveRaw(const std::string &key) {
for (auto &conf : configs_) {
if (conf.writeable) {
conf.cfg->RemoveRaw(key);
}
}
}
int LayeredConfiguration::lowest() const {
if (configs_.empty()) {
return 0;
} else {
return configs_.front().priority - 1;
}
}
int LayeredConfiguration::highest() const {
if (configs_.empty()) {
return 0;
} else {
return configs_.back().priority + 1;
}
}
} // namespace tile
}// namespace tile

View File

@ -9,47 +9,46 @@
namespace tile {
class LayeredConfiguration : public Configuration {
public:
using Ptr = RefPtr<LayeredConfiguration>;
LayeredConfiguration();
LayeredConfiguration(const LayeredConfiguration &) = delete;
LayeredConfiguration &operator=(const LayeredConfiguration &) = delete;
using Ptr = RefPtr<LayeredConfiguration>;
LayeredConfiguration();
LayeredConfiguration(const LayeredConfiguration &) = delete;
LayeredConfiguration &operator=(const LayeredConfiguration &) = delete;
void Add(Configuration::Ptr cfg);
void Add(Configuration::Ptr cfg, int priority);
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, int priority, bool writeable);
void AddWriteable(Configuration::Ptr cfg, int priority);
Configuration::Ptr Find(const Slice &label) const;
void RemoveConfig(Configuration::Ptr cfg);
void Add(Configuration::Ptr cfg);
void Add(Configuration::Ptr cfg, int priority);
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, int priority, bool writeable);
void AddWriteable(Configuration::Ptr cfg, int priority);
Configuration::Ptr Find(const Slice &label) const;
void RemoveConfig(Configuration::Ptr cfg);
protected:
struct ConfigItem {
Configuration::Ptr cfg;
// ... > -2 > -1 > 0 > 1 > 2 > 3 > 4 ...
int priority;
// can remove or set new?
bool writeable;
std::string label;
};
struct ConfigItem {
Configuration::Ptr cfg;
// ... > -2 > -1 > 0 > 1 > 2 > 3 > 4 ...
int priority;
// can remove or set new?
bool writeable;
std::string label;
};
bool GetRaw(const Slice &key, std::string *value) const override;
bool SetRaw(const Slice &key, const Slice &value) override;
Configuration::Keys Enumerate(const Slice &key) const override;
void RemoveRaw(const std::string &key);
bool GetRaw(const Slice &key, std::string *value) const override;
bool SetRaw(const Slice &key, const Slice &value) override;
Configuration::Keys Enumerate(const Slice &key) const override;
void RemoveRaw(const std::string &key);
int lowest() const;
int highest() const;
int lowest() const;
int highest() const;
~LayeredConfiguration();
~LayeredConfiguration();
private:
using ConfigList = std::list<ConfigItem>;
ConfigList configs_;
using ConfigList = std::list<ConfigItem>;
ConfigList configs_;
};
} // namespace tile
}// namespace tile
#endif // TILE_BASE_CONFIG_LAYERED_CONFIGURATION_H
#endif// TILE_BASE_CONFIG_LAYERED_CONFIGURATION_H

View File

@ -8,4 +8,4 @@
#include "tile/base/config/ini_file_configuration.h"
#include "tile/base/config/layered_configuration.h"
#endif // TILE_BASE_CONFIGURATION_H
#endif// TILE_BASE_CONFIGURATION_H

View File

@ -4,4 +4,4 @@
#pragma once
#include "tile/base/data/json.h"
#endif // TILE_BASE_DATA_H
#endif// TILE_BASE_DATA_H

View File

@ -11,28 +11,34 @@
namespace tile {
template <typename T>
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_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> {
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> {
return Format("{}", value);
template<typename T>
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> Json::Value ToJson(const std::atomic<T> &v) {
return ToJson(v.load(std::memory_order_relaxed));
template<typename T>
auto
ToJson(const T &value) -> enable_if_t<!std::is_signed<T>::value && !std::is_unsigned<T>::value, Json::Value>
{
return Format("{}", value);
}
} // namespace tile
template<typename T>
Json::Value
ToJson(const std::atomic<T> &v)
{
return ToJson(v.load(std::memory_order_relaxed));
}
#endif // TILE_BASE_DATA_JSON_H
}// namespace tile
#endif// TILE_BASE_DATA_JSON_H

View File

@ -9,56 +9,57 @@ namespace tile {
class ScopedDeferred {
public:
template <typename F>
explicit ScopedDeferred(F &&f) : action_(std::move(f)) {}
template<typename F>
explicit ScopedDeferred(F &&f) : action_(std::move(f))
{}
~ScopedDeferred() { action_(); }
ScopedDeferred(const ScopedDeferred &) = delete;
ScopedDeferred &operator=(const ScopedDeferred &) = delete;
~ScopedDeferred() { action_(); }
ScopedDeferred(const ScopedDeferred &) = delete;
ScopedDeferred &operator=(const ScopedDeferred &) = delete;
private:
std::function<void()> action_;
std::function<void()> action_;
};
class Deferred {
public:
Deferred() = default;
template <typename F>
explicit Deferred(F &&f) : action_(std::forward<F>(f)) {}
Deferred() = default;
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;
}
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_; }
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; }
void Dimiss() noexcept { action_ = nullptr; }
private:
std::function<void()> action_;
std::function<void()> action_;
};
} // namespace tile
}// namespace tile
#endif // _TILE_BASE_DEFERED_H
#endif// _TILE_BASE_DEFERED_H

View File

@ -2,49 +2,51 @@
#include "gtest/gtest.h"
namespace tile {
TEST(ScopedDefered, All) {
bool f = false;
{
ScopedDeferred scoped_defered([&] { f = true; });
ASSERT_FALSE(f);
}
ASSERT_TRUE(f);
TEST(ScopedDefered, All)
{
bool f = false;
{
ScopedDeferred scoped_defered([&] { f = true; });
ASSERT_FALSE(f);
}
ASSERT_TRUE(f);
}
TEST(Defered, All) {
bool f1 = false;
bool f2 = false;
{
Deferred defer([&] { f1 = true; });
ASSERT_FALSE(f1);
Deferred defer2([&] { f2 = true; });
defer2.Dimiss();
TEST(Defered, All)
{
bool f1 = false;
bool f2 = false;
{
Deferred defer([&] { f1 = true; });
ASSERT_FALSE(f1);
Deferred defer2([&] { f2 = true; });
defer2.Dimiss();
ASSERT_FALSE(f2);
}
ASSERT_TRUE(f1);
ASSERT_FALSE(f2);
}
ASSERT_TRUE(f1);
ASSERT_FALSE(f2);
bool f3 = false;
Deferred defer3([&] { f3 = true; });
ASSERT_FALSE(f3);
defer3.Fire();
ASSERT_TRUE(f3);
bool f3 = false;
Deferred defer3([&] { f3 = true; });
ASSERT_FALSE(f3);
defer3.Fire();
ASSERT_TRUE(f3);
bool f4 = false;
Deferred defer5;
{
Deferred defer4([&] { f4 = true; });
bool f4 = false;
Deferred defer5;
{
Deferred defer4([&] { f4 = true; });
ASSERT_FALSE(f4);
defer5 = std::move(defer4);
}
ASSERT_FALSE(f4);
defer5 = std::move(defer4);
}
ASSERT_FALSE(f4);
auto defer6 = std::move(defer5);
defer5.Fire();
ASSERT_FALSE(defer5);
defer6.Fire();
ASSERT_TRUE(f4);
auto defer6 = std::move(defer5);
defer5.Fire();
ASSERT_FALSE(defer5);
defer6.Fire();
ASSERT_TRUE(f4);
Deferred().Fire();
Deferred().Dimiss();
Deferred().Fire();
Deferred().Dimiss();
}
} // namespace tile
}// namespace tile

View File

@ -10,21 +10,21 @@
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;
}
return demangled;
#elif defined(__GNUC__) || defined(__clang__)
int status;
char *demangled = abi::__cxa_demangle(s, nullptr, nullptr, &status);
ScopedDeferred _([&] { free(demangled); });
if (!demangled) { return s; }
return demangled;
#else
#error "Demangle not supported on current compiler."
#endif
}
} // namespace tile
}// namespace tile

View File

@ -11,8 +11,11 @@ namespace tile {
std::string Demangle(const char *s);
template <class T> std::string GetTypeName() {
return Demangle(typeid(T).name());
template<class T>
std::string
GetTypeName()
{
return Demangle(typeid(T).name());
}
#if __GNUC__ >= 12
@ -20,14 +23,17 @@ template <class T> std::string GetTypeName() {
#pragma GCC diagnostic ignored "-Wnonnull-compare"
#endif
template <class T> std::string GetTypeName(T &&o) {
return Demangle(typeid(std::forward<T>(o)).name());
template<class T>
std::string
GetTypeName(T &&o)
{
return Demangle(typeid(std::forward<T>(o)).name());
}
#if __GNUC__ >= 12
#pragma GCC diagnostic pop
#endif
} // namespace tile
}// namespace tile
#endif // _TILE_BASE_DEMANGLE_H
#endif// _TILE_BASE_DEMANGLE_H

View File

@ -3,16 +3,16 @@
namespace tile {
struct C {
struct D {
struct E {};
};
struct D {
struct E {};
};
};
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 !@#$"));
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 !@#$"));
}
} // namespace tile
}// namespace tile

View File

@ -21,186 +21,174 @@
#include <utility>
// class dependency
#define TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \
extern ::tile::detail::dependency_registry::ClassRegistry< \
struct Registry, Interface, ##__VA_ARGS__> &Registry
#define TILE_DECLARE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \
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::internal::LazyInit<::tile::NeverDestroyed< \
::tile::detail::dependency_registry::ClassRegistry< \
struct Registry, Interface, ##__VA_ARGS__>>>()
#define TILE_DEFINE_CLASS_DEPENDENCY_REGISTRY(Registry, Interface, ...) \
::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__>>>()
#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__))
#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__))
#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>>() \
->Get(); \
::tile::detail::dependency_registry::AddToRegistry(registry, Name, \
Factory); \
}
#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>>() \
->Get(); \
::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
#define TILE_DECLARE_OBJECT_DEPENDENCY_REGISTRY(Registry, Interface) \
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>>>()
#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>>>()
#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>>() \
->Get(); \
::tile::detail::dependency_registry::AddToRegistry(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>>() \
->Get(); \
::tile::detail::dependency_registry::AddToRegistry(registry, ObjectName, PointerOrFactory); \
}
namespace tile {
namespace detail {
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) {
return make_unique<Interface>(std::forward<Args>(args)...);
#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)
{
return make_unique<Interface>(std::forward<Args>(args)...);
}
template <typename T, typename... Args>
void AddToRegistry(T *registry, Args &&...args) {
registry->Register(std::forward<Args>(args)...);
template<typename T, typename... Args>
void
AddToRegistry(T *registry, Args &&...args)
{
registry->Register(std::forward<Args>(args)...);
}
template <typename Tag, typename Interface, typename... FactoryArgs>
template<typename Tag, typename Interface, typename... FactoryArgs>
class ClassRegistry {
using Factory = std::function<std::unique_ptr<Interface>(FactoryArgs...)>;
using Factory = std::function<std::unique_ptr<Interface>(FactoryArgs...)>;
public:
Factory TryGetFactory(Slice name) const noexcept {
auto iter = factories_.find(name.ToString());
if (iter == factories_.end()) {
return nullptr;
Factory TryGetFactory(Slice name) const noexcept
{
auto iter = factories_.find(name.ToString());
if (iter == factories_.end()) { return nullptr; }
auto constructor_ptr = iter->second.get();
return
[constructor_ptr](FactoryArgs... args) { return (*constructor_ptr)(std::forward<FactoryArgs>(args)...); };
}
auto constructor_ptr = iter->second.get();
return [constructor_ptr](FactoryArgs... args) {
return (*constructor_ptr)(std::forward<FactoryArgs>(args)...);
Factory GetFactory(const std::string &name) const noexcept
{
auto result = TryGetFactory(name);
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName<Interface>());
return result;
}
std::unique_ptr<Interface> TryNew(Slice name, FactoryArgs... args) const
{
auto iter = factories_.find(name.ToString());
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
{
auto result = TryNew(name, std::forward<FactoryArgs>(args)...);
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.", name, GetTypeName<Interface>());
return result;
}
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: ");
factories_[name] = make_unique<Factory>(std::move(factory));
}
private:
std::unordered_map<std::string, std::unique_ptr<Factory>> factories_;
};
template<typename Tag, typename Interface>
class ObjectRegistry {
public:
Interface *TryGet(Slice name) const
{
auto iter = objects_.find(name.ToString());
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
{
auto result = TryGet(name);
TILE_CHECK(result, "Object dependency [{}] implementing interface [{}] is not found", name,
GetTypeName<Interface>());
return result;
}
private:
template<typename T, typename... Args>
friend void AddToRegistry(T *registry, Args &&...args);
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); });
}
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);
}
private:
struct LazilyInstantiatedObject {
std::once_flag flag;
MaybeOwning<Interface> object;
std::function<MaybeOwning<Interface>()> initializer;
};
}
Factory GetFactory(const std::string &name) const noexcept {
auto result = TryGetFactory(name);
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.",
name, GetTypeName<Interface>());
return result;
}
std::unique_ptr<Interface> TryNew(Slice name, FactoryArgs... args) const {
auto iter = factories_.find(name.ToString());
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 {
auto result = TryNew(name, std::forward<FactoryArgs>(args)...);
TILE_CHECK(result, "Class [{}] implement interface [{}] is not found.",
name, GetTypeName<Interface>());
return result;
}
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: ");
factories_[name] = make_unique<Factory>(std::move(factory));
}
private:
std::unordered_map<std::string, std::unique_ptr<Factory>> factories_;
std::unordered_map<std::string, std::unique_ptr<LazilyInstantiatedObject>> objects_;
};
template <typename Tag, typename Interface> class ObjectRegistry {
public:
Interface *TryGet(Slice name) const {
auto iter = objects_.find(name.ToString());
if (iter == objects_.end()) {
return nullptr;
}
auto &&e = *iter->second;
std::call_once(e.flag, [&] { e.object = e.initializer(); });
return e.object.Get();
}
}// namespace dependency_registry
}// namespace detail
}// namespace tile
Interface *Get(Slice name) const {
auto result = TryGet(name);
TILE_CHECK(
result,
"Object dependency [{}] implementing interface [{}] is not found", name,
GetTypeName<Interface>());
return result;
}
private:
template <typename T, typename... Args>
friend void AddToRegistry(T *registry, Args &&...args);
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);
});
}
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);
}
private:
struct LazilyInstantiatedObject {
std::once_flag flag;
MaybeOwning<Interface> object;
std::function<MaybeOwning<Interface>()> initializer;
};
std::unordered_map<std::string, std::unique_ptr<LazilyInstantiatedObject>>
objects_;
};
} // namespace dependency_registry
} // namespace detail
} // namespace tile
#endif // TILE_BASE_DEPENDENCY_REGISTRY_H
#endif// TILE_BASE_DEPENDENCY_REGISTRY_H

View File

@ -1,77 +1,84 @@
#include "tile/base/dependency_registry.h"
#include "gtest/gtest.h"
namespace tile {
struct Destroyer {
virtual ~Destroyer() = default;
virtual ~Destroyer() = default;
};
struct FastDestroyer : Destroyer {
FastDestroyer() { ++instances; }
~FastDestroyer() override { --instances; }
static int instances;
FastDestroyer() { ++instances; }
~FastDestroyer() override { --instances; }
static int instances;
};
int FastDestroyer::instances = 0;
struct GentleDestroyer : Destroyer {
GentleDestroyer() { ++instances; }
~GentleDestroyer() override { --instances; }
static int instances;
GentleDestroyer() { ++instances; }
~GentleDestroyer() override { --instances; }
static int instances;
};
int GentleDestroyer::instances = 0;
struct SpeedDestroyer : Destroyer {
explicit SpeedDestroyer(int) {}
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>();
return make_unique<GentleDestroyer>();
});
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) {
return make_unique<SpeedDestroyer2>();
});
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) {
EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer"));
EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer"));
EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));
EXPECT_TRUE(world_destroyer.TryNew("gentle-destroyer"));
EXPECT_TRUE(world_destroyer.TryNew("fast-destroyer"));
EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));
TEST(DependencyRegistry, Class)
{
EXPECT_TRUE(world_destroyer.TryGetFactory("gentle-destroyer"));
EXPECT_TRUE(world_destroyer.TryGetFactory("fast-destroyer"));
EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));
EXPECT_TRUE(world_destroyer.TryNew("gentle-destroyer"));
EXPECT_TRUE(world_destroyer.TryNew("fast-destroyer"));
EXPECT_FALSE(world_destroyer.TryGetFactory("404-destroyer"));
EXPECT_EQ(1, GentleDestroyer::instances); // The global one.
EXPECT_EQ(0, FastDestroyer::instances);
{
auto gentle = world_destroyer.TryNew("gentle-destroyer");
EXPECT_EQ(2, GentleDestroyer::instances);
EXPECT_EQ(1, GentleDestroyer::instances);// The global one.
EXPECT_EQ(0, FastDestroyer::instances);
{
auto gentle = world_destroyer.TryNew("gentle-destroyer");
EXPECT_EQ(2, GentleDestroyer::instances);
EXPECT_EQ(0, FastDestroyer::instances);
auto fast = world_destroyer.TryNew("fast-destroyer");
EXPECT_EQ(2, GentleDestroyer::instances);
EXPECT_EQ(1, FastDestroyer::instances);
}
EXPECT_EQ(1, GentleDestroyer::instances);
EXPECT_EQ(0, FastDestroyer::instances);
auto fast = world_destroyer.TryNew("fast-destroyer");
EXPECT_EQ(2, GentleDestroyer::instances);
EXPECT_EQ(1, FastDestroyer::instances);
}
EXPECT_EQ(1, GentleDestroyer::instances);
EXPECT_EQ(0, FastDestroyer::instances);
}
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"));
EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer", 456));
EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer-2", 456));
EXPECT_FALSE(with_arg_destroyer.TryNew("speed-destroyer-3", 456));
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"));
EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer", 456));
EXPECT_TRUE(with_arg_destroyer.TryNew("speed-destroyer-2", 456));
EXPECT_FALSE(with_arg_destroyer.TryNew("speed-destroyer-3", 456));
}
} // namespace tile
}// namespace tile

View File

@ -8,18 +8,22 @@
#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) {
TILE_DCHECK(dynamic_cast<R>(ptr));
return static_cast<R>(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)) {
return down_cast<To>(&ref);
template<typename To, typename From>
inline auto
down_cast(From &ref) -> decltype(down_cast(&ref))
{
return down_cast<To>(&ref);
}
} // namespace tile
}// namespace tile
#endif // _TILE_BASE_DOWN_CAST_H
#endif// _TILE_BASE_DOWN_CAST_H

View File

@ -3,22 +3,25 @@
namespace tile {
struct A {
virtual ~A() = default;
virtual ~A() = default;
};
struct B : A {};
TEST(DownCast, All) {
B b;
A *ptr = &b;
ASSERT_NE(nullptr, down_cast<B>(ptr));
ASSERT_NE(nullptr, down_cast<B>(const_cast<const A *>(ptr)));
TEST(DownCast, All)
{
B b;
A *ptr = &b;
ASSERT_NE(nullptr, down_cast<B>(ptr));
ASSERT_NE(nullptr, down_cast<B>(const_cast<const A *>(ptr)));
}
#ifndef NDEBUG
TEST(CastDeathTest, DownCast) {
A a;
A *ptr = &a;
ASSERT_DEATH(down_cast<B>(ptr), "");
TEST(CastDeathTest, DownCast)
{
A a;
A *ptr = &a;
ASSERT_DEATH(down_cast<B>(ptr), "");
}
#endif
} // namespace tile
}// namespace tile

View File

@ -7,4 +7,4 @@
#include "tile/base/encoding/hex.h"
#include "tile/base/encoding/percent.h"
#endif // _TILE_BASE_ENCODING_H
#endif// _TILE_BASE_ENCODING_H

View File

@ -19,137 +19,132 @@ static constexpr std::size_t kBufferSize = BLOCK_SIZE_1M;
#undef BLOCK_SIZE_64K
#undef BLOCK_SIZE_1M
} // namespace
}// namespace
std::string EncodeBase64(Slice from) {
std::string result;
EncodeBase64(from, &result);
return result;
}
std::optional<std::string> DecodeBase64(Slice from) {
std::string result;
if (!DecodeBase64(from, &result)) {
return std::nullopt;
}
return result;
std::string
EncodeBase64(Slice from)
{
std::string result;
EncodeBase64(from, &result);
return result;
}
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());
const auto size = 4 * ((from.size() + 2) / 3);
to->resize(size);
base64_encodestate state;
base64_init_encodestate(&state);
std::size_t used = 0;
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);
from.RemovePrefix(input_len);
};
base64_encode_blockend(
reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used,
&state);
// bool success =
// Base64::Encode(from.data(), from.size(),
// internal::RemoveConstPtr(to->data()), to->size());
// TILE_CHECK(success, "Unexpected: Failed to do base64-encode.");
std::optional<std::string>
DecodeBase64(Slice from)
{
std::string result;
if (!DecodeBase64(from, &result)) { return std::nullopt; }
return result;
}
bool DecodeBase64(Slice from, std::string *to) {
if (TILE_UNLIKELY(from.empty())) {
to->clear();
return true;
}
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());
std::string safe_from_str;
// auto padding
auto padding = (4 - from.size() % 4) % 4;
if (padding > 2) {
return false;
} else if (padding != 0) {
TILE_LOG_WARNING("`from.size()` [{}] Can not be divisible by 4. "
"auto add padding [{}] `=`",
from.size(), padding);
safe_from_str = from.ToString() + std::string(padding, '=');
from = safe_from_str;
}
const auto size = 4 * ((from.size() + 2) / 3);
to->resize(size);
base64_encodestate state;
base64_init_encodestate(&state);
auto size = 3 * from.size() / 4;
to->resize(size);
{
Slice input = from;
base64_decodestate state;
base64_init_decodestate(&state);
std::size_t used = 0;
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;
}
used += encoded_len;
input.RemovePrefix(input_len);
}
}
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);
from.RemovePrefix(input_len);
};
base64_encode_blockend(reinterpret_cast<char *>(internal::RemoveConstPtr(to->data())) + used, &state);
// === Sample
// {
// bool success =
// Base64::Decode(from.data(), from.size(),
// internal::RemoveConstPtr(to->data()), to->size());
// if (!success) {
// return false;
// }
// }
// === OpenSSL
// int rc;
// rc = EVP_DecodeBlock(
// reinterpret_cast<unsigned char
// *>(internal::RemoveConstPtr(to->data())), reinterpret_cast<const
// unsigned char *>(from.data()), from.size());
// if (rc != size) {
// return false;
// }
//
// @sa: https://tools.ietf.org/html/rfc4648#section-4
//
// (1) The final quantum of encoding input is an integral multiple of 24
// bits; here, the final unit of encoded output will be an integral
// multiple of 4 characters with no "=" padding.
//
// (2) The final quantum of encoding input is exactly 8 bits; here, the
// final unit of encoded output will be two characters followed by
// two "=" padding characters.
//
// (3) The final quantum of encoding input is exactly 16 bits; here, the
// 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() - 2] == '=') {
to->pop_back();
}
// to->pop_back(); // Remove Terminating null
return true;
// bool success =
// Base64::Encode(from.data(), from.size(),
// internal::RemoveConstPtr(to->data()), to->size());
// TILE_CHECK(success, "Unexpected: Failed to do base64-encode.");
}
} // namespace tile
bool
DecodeBase64(Slice from, std::string *to)
{
if (TILE_UNLIKELY(from.empty())) {
to->clear();
return true;
}
std::string safe_from_str;
// auto padding
auto padding = (4 - from.size() % 4) % 4;
if (padding > 2) {
return false;
} else if (padding != 0) {
TILE_LOG_WARNING("`from.size()` [{}] Can not be divisible by 4. "
"auto add padding [{}] `=`",
from.size(), padding);
safe_from_str = from.ToString() + std::string(padding, '=');
from = safe_from_str;
}
auto size = 3 * from.size() / 4;
to->resize(size);
{
Slice input = from;
base64_decodestate state;
base64_init_decodestate(&state);
std::size_t used = 0;
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; }
used += encoded_len;
input.RemovePrefix(input_len);
}
}
// === Sample
// {
// bool success =
// Base64::Decode(from.data(), from.size(),
// internal::RemoveConstPtr(to->data()), to->size());
// if (!success) {
// return false;
// }
// }
// === OpenSSL
// int rc;
// rc = EVP_DecodeBlock(
// reinterpret_cast<unsigned char
// *>(internal::RemoveConstPtr(to->data())), reinterpret_cast<const
// unsigned char *>(from.data()), from.size());
// if (rc != size) {
// return false;
// }
//
// @sa: https://tools.ietf.org/html/rfc4648#section-4
//
// (1) The final quantum of encoding input is an integral multiple of 24
// bits; here, the final unit of encoded output will be an integral
// multiple of 4 characters with no "=" padding.
//
// (2) The final quantum of encoding input is exactly 8 bits; here, the
// final unit of encoded output will be two characters followed by
// two "=" padding characters.
//
// (3) The final quantum of encoding input is exactly 16 bits; here, the
// 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() - 2] == '=') { to->pop_back(); }
// to->pop_back(); // Remove Terminating null
return true;
}
}// namespace tile

View File

@ -13,6 +13,6 @@ std::optional<std::string> DecodeBase64(Slice from);
void EncodeBase64(Slice from, std::string *to);
bool DecodeBase64(Slice from, std::string *to);
} // namespace tile
}// namespace tile
#endif // _TILE_BASE_ENCODING_BASE64_H
#endif// _TILE_BASE_ENCODING_BASE64_H

View File

@ -9,70 +9,75 @@
namespace tile {
static constexpr auto kText = ".<>@????????";
static constexpr auto kBase64Text = "Ljw+QD8/Pz8/Pz8/";
static constexpr auto kText2 = ".<>@???????";
static constexpr auto kText = ".<>@????????";
static constexpr auto kBase64Text = "Ljw+QD8/Pz8/Pz8/";
static constexpr auto kText2 = ".<>@???????";
static constexpr auto kBase64Text2 = "Ljw+QD8/Pz8/Pz8=";
TEST(Base64, Default) {
EXPECT_EQ(kBase64Text, EncodeBase64(kText));
auto decoded = DecodeBase64(kBase64Text);
ASSERT_TRUE(decoded);
EXPECT_EQ(kText, *decoded);
EXPECT_FALSE(DecodeBase64("some-invalid-base64-encoded!!"));
}
TEST(Base64, Padding) {
EXPECT_EQ(kBase64Text2, EncodeBase64(kText2));
auto decoded = DecodeBase64(kBase64Text2);
ASSERT_TRUE(decoded);
EXPECT_EQ(kText2, *decoded);
}
TEST(Base64, Empty) {
EXPECT_EQ("", EncodeBase64(""));
auto decoded = DecodeBase64("");
ASSERT_TRUE(decoded);
EXPECT_EQ("", *decoded);
}
TEST(Base64, AutoPadding) {
static constexpr auto kChar = "A";
EXPECT_EQ("QQ==", EncodeBase64(kChar));
{
auto decoded = DecodeBase64("QQ==");
TEST(Base64, Default)
{
EXPECT_EQ(kBase64Text, EncodeBase64(kText));
auto decoded = DecodeBase64(kBase64Text);
ASSERT_TRUE(decoded);
EXPECT_EQ(kChar, *decoded);
}
// auto padding, 1
{
auto decoded1 = DecodeBase64("QQ=");
ASSERT_TRUE(decoded1);
EXPECT_EQ(kChar, *decoded1);
}
// auto padding, 2
{
auto decoded2 = DecodeBase64("QQ");
ASSERT_TRUE(decoded2);
EXPECT_EQ(kChar, *decoded2);
}
EXPECT_EQ(kText, *decoded);
EXPECT_FALSE(DecodeBase64("some-invalid-base64-encoded!!"));
}
TEST(Base64, Torture) {
std::string random_str;
std::string encoded;
std::string decoded;
for (int i = 0; i != 1000; ++i) {
testing::RandomString(&random_str, 10000, 10000);
EncodeBase64(random_str, &encoded);
ASSERT_TRUE(DecodeBase64(encoded, &decoded));
EXPECT_EQ(random_str, decoded);
}
TEST(Base64, Padding)
{
EXPECT_EQ(kBase64Text2, EncodeBase64(kText2));
auto decoded = DecodeBase64(kBase64Text2);
ASSERT_TRUE(decoded);
EXPECT_EQ(kText2, *decoded);
}
} // namespace tile
TEST(Base64, Empty)
{
EXPECT_EQ("", EncodeBase64(""));
auto decoded = DecodeBase64("");
ASSERT_TRUE(decoded);
EXPECT_EQ("", *decoded);
}
TEST(Base64, AutoPadding)
{
static constexpr auto kChar = "A";
EXPECT_EQ("QQ==", EncodeBase64(kChar));
{
auto decoded = DecodeBase64("QQ==");
ASSERT_TRUE(decoded);
EXPECT_EQ(kChar, *decoded);
}
// auto padding, 1
{
auto decoded1 = DecodeBase64("QQ=");
ASSERT_TRUE(decoded1);
EXPECT_EQ(kChar, *decoded1);
}
// auto padding, 2
{
auto decoded2 = DecodeBase64("QQ");
ASSERT_TRUE(decoded2);
EXPECT_EQ(kChar, *decoded2);
}
}
TEST(Base64, Torture)
{
std::string random_str;
std::string encoded;
std::string decoded;
for (int i = 0; i != 1000; ++i) {
testing::RandomString(&random_str, 10000, 10000);
EncodeBase64(random_str, &encoded);
ASSERT_TRUE(DecodeBase64(encoded, &decoded));
EXPECT_EQ(random_str, decoded);
}
}
}// namespace tile

View File

@ -3,286 +3,247 @@
#include <string>
const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
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;
return 255;
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);
}
}
int kBase64Lookup[256];
struct Base64LookupInitializer {
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 Base64LookupInitializer init;
return init.kBase64Lookup[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);
}
} // namespace
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) {
int i = 0, j = 0;
size_t enc_len = 0;
unsigned char a3[3];
unsigned char a4[4];
static bool Encode(const std::string &in, std::string *out)
{
int i = 0, j = 0;
size_t enc_len = 0;
unsigned char a3[3];
unsigned char a4[4];
out->resize(EncodedLength(in));
out->resize(EncodedLength(in));
size_t input_len = in.size();
std::string::const_iterator input = in.begin();
size_t input_len = in.size();
std::string::const_iterator input = in.begin();
while (input_len--) {
a3[i++] = *(input++);
if (i == 3) {
a3_to_a4(a4, a3);
while (input_len--) {
a3[i++] = *(input++);
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;
}
}
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) { a3[j] = '\0'; }
if (i) {
for (j = i; j < 3; j++) {
a3[j] = '\0';
}
a3_to_a4(a4, a3);
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++] = '=';
}
}
return (enc_len == out->size());
}
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];
unsigned char a4[4];
size_t encoded_length = EncodedLength(input_length);
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]];
}
// out[0] = kBase64Alphabet[a4[0]];
// out[1] = kBase64Alphabet[a4[1]];
// out[2] = kBase64Alphabet[a4[2]];
// out[3] = kBase64Alphabet[a4[3]];
// out += 4;
i = 0;
}
}
if (i) {
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]];
}
while ((i++ < 3)) {
*out++ = '=';
}
}
return (out == (out_begin + encoded_length));
}
static bool Decode(const std::string &in, std::string *out) {
int i = 0, j = 0;
size_t dec_len = 0;
unsigned char a3[3];
unsigned char a4[4];
size_t input_len = in.size();
std::string::const_iterator input = in.begin();
out->resize(DecodedLength(in));
while (input_len--) {
if (*input == '=') {
break;
}
a4[i++] = *(input++);
if (i == 4) {
for (i = 0; i < 4; i++) {
a4[i] = b64_lookup(a4[i]);
while ((i++ < 3)) { (*out)[enc_len++] = '='; }
}
a4_to_a3(a3, a4);
return (enc_len == out->size());
}
for (i = 0; i < 3; i++) {
(*out)[dec_len++] = a3[i];
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];
unsigned char a4[4];
size_t encoded_length = EncodedLength(input_length);
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]]; }
// out[0] = kBase64Alphabet[a4[0]];
// out[1] = kBase64Alphabet[a4[1]];
// out[2] = kBase64Alphabet[a4[2]];
// out[3] = kBase64Alphabet[a4[3]];
// out += 4;
i = 0;
}
}
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) { a3[j] = '\0'; }
if (i) {
for (j = i; j < 4; j++) {
a4[j] = '\0';
}
a3_to_a4(a4, a3);
for (j = 0; j < 4; j++) {
a4[j] = b64_lookup(a4[j]);
}
for (j = 0; j < i + 1; j++) { *out++ = kBase64Alphabet[a4[j]]; }
a4_to_a3(a3, a4);
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) {
int i = 0, j = 0;
char *out_begin = out;
unsigned char a3[3];
unsigned char a4[4];
size_t decoded_length = DecodedLength(input, input_length);
if (out_length < decoded_length)
return false;
while (input_length--) {
if (*input == '=') {
break;
}
a4[i++] = *(input++);
if (i == 4) {
for (i = 0; i < 4; i++) {
a4[i] = b64_lookup(a4[i]);
while ((i++ < 3)) { *out++ = '='; }
}
a4_to_a3(a3, a4);
return (out == (out_begin + encoded_length));
}
for (i = 0; i < 3; i++) {
*out++ = a3[i];
static bool Decode(const std::string &in, std::string *out)
{
int i = 0, j = 0;
size_t dec_len = 0;
unsigned char a3[3];
unsigned char a4[4];
size_t input_len = in.size();
std::string::const_iterator input = in.begin();
out->resize(DecodedLength(in));
while (input_len--) {
if (*input == '=') { break; }
a4[i++] = *(input++);
if (i == 4) {
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]; }
i = 0;
}
}
i = 0;
}
if (i) {
for (j = i; j < 4; j++) { a4[j] = '\0'; }
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]; }
}
return (dec_len == out->size());
}
if (i) {
for (j = i; j < 4; j++) {
a4[j] = '\0';
}
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];
unsigned char a4[4];
for (j = 0; j < 4; j++) {
a4[j] = b64_lookup(a4[j]);
}
size_t decoded_length = DecodedLength(input, input_length);
a4_to_a3(a3, a4);
if (out_length < decoded_length) return false;
for (j = 0; j < i - 1; j++) {
*out++ = a3[j];
}
while (input_length--) {
if (*input == '=') { break; }
a4[i++] = *(input++);
if (i == 4) {
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]; }
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++) { a4[j] = '\0'; }
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]; }
}
return (out == (out_begin + decoded_length));
}
return (out == (out_begin + decoded_length));
}
static size_t DecodedLength(const char *in, size_t in_length)
{
int numEq = 0;
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;
const char *in_end = in + in_length;
while (*--in_end == '=')
++numEq;
return ((6 * in_length) / 8) - numEq;
}
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;
return ((6 * in_length) / 8) - numEq;
}
return ((6 * n) / 8) - numEq;
}
static size_t DecodedLength(const std::string &in)
{
int numEq = 0;
size_t n = in.size();
inline static size_t EncodedLength(size_t length) {
return (length + 2 - ((length + 2) % 3)) / 3 * 4;
}
for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) { ++numEq; }
inline static size_t EncodedLength(const std::string &in) {
return EncodedLength(in.length());
}
return ((6 * n) / 8) - numEq;
}
inline static void StripPadding(std::string *in) {
while (!in->empty() && *(in->rbegin()) == '=')
in->resize(in->size() - 1);
}
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 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) {
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 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) {
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];
}
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];
}
};
#endif // BASE64_H
#endif// BASE64_H

View File

@ -6,80 +6,88 @@ namespace detail {
namespace {
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;
static constexpr auto kSize = kCharMax - kCharMin + 1;
int AsciiCodeFromCharPairSlow(char a, char b) {
a = ToLower(a);
b = ToLower(b);
auto ToNum = [](char x) {
if (x >= '0' && x <= '9') {
return x - '0';
} else if (x >= 'a' && x <= 'f') {
return x - 'a' + 10;
int
AsciiCodeFromCharPairSlow(char a, char b)
{
a = ToLower(a);
b = ToLower(b);
auto ToNum = [](char x) {
if (x >= '0' && x <= '9') {
return x - '0';
} else if (x >= 'a' && x <= 'f') {
return x - 'a' + 10;
} else {
return -1;
}
};
int x = ToNum(a);
int y = ToNum(b);
if (x == -1 || y == -1) { return -1; }
return (x << 4) | y;
}
std::pair<char, char>
AsciiCodeToCharPairSlow(std::uint8_t value, bool uppercase)
{
if (uppercase) {
return std::make_pair(kHexCharsUpper[value >> 4], kHexCharsUpper[value & 0x0f]);
} else {
return -1;
return std::make_pair(kHexCharsLower[value >> 4], kHexCharsLower[value & 0x0f]);
}
};
int x = ToNum(a);
int y = ToNum(b);
if (x == -1 || y == -1) {
return -1;
}
return (x << 4) | y;
}
std::pair<char, char> AsciiCodeToCharPairSlow(std::uint8_t value,
bool uppercase) {
if (uppercase) {
return std::make_pair(kHexCharsUpper[value >> 4],
kHexCharsUpper[value & 0x0f]);
} else {
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> table;
for (char i = kCharMin; i != kCharMax; ++i) {
for (char j = kCharMin; j != kCharMax; ++j) {
table[i - kCharMin][j - kCharMin] = AsciiCodeFromCharPairSlow(i, j);
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) {
table[i - kCharMin][j - kCharMin] = AsciiCodeFromCharPairSlow(i, j);
}
}
}
return table;
return table;
}
std::array<std::pair<char, char>, 256>
GenAsciiCodeToCharPairTable(bool uppercase) {
std::array<std::pair<char, char>, 256> table;
for (int i = 0; i != 256; ++i) {
table[i] = AsciiCodeToCharPairSlow(i, uppercase);
}
return table;
GenAsciiCodeToCharPairTable(bool uppercase)
{
std::array<std::pair<char, char>, 256> table;
for (int i = 0; i != 256; ++i) { table[i] = AsciiCodeToCharPairSlow(i, uppercase); }
return table;
}
} // namespace
}// namespace
int AsciiCodeFromCharPair(char a, char b) {
static auto table = GenAsciiCodeFromCharPairTable();
return table[a - kCharMin][b - kCharMin];
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) {
static auto table = GenAsciiCodeToCharPairTable(true);
return table[value];
}
inline std::pair<char, char> AsciiCodeToLowerCharPair(std::uint8_t value) {
static auto table = GenAsciiCodeToCharPairTable(false);
return table[value];
inline std::pair<char, char>
AsciiCodeToUpperCharPair(std::uint8_t value)
{
static auto table = GenAsciiCodeToCharPairTable(true);
return table[value];
}
std::pair<char, char> AsciiCodeToCharPair(std::uint8_t value, bool uppercase) {
return uppercase ? AsciiCodeToUpperCharPair(value)
: AsciiCodeToLowerCharPair(value);
inline std::pair<char, char>
AsciiCodeToLowerCharPair(std::uint8_t value)
{
static auto table = GenAsciiCodeToCharPairTable(false);
return table[value];
}
} // namespace detail
} // namespace tile
std::pair<char, char>
AsciiCodeToCharPair(std::uint8_t value, bool uppercase)
{
return uppercase ? AsciiCodeToUpperCharPair(value) : AsciiCodeToLowerCharPair(value);
}
}// namespace detail
}// namespace tile

View File

@ -13,7 +13,7 @@ namespace tile {
namespace detail {
struct CharPair {
char a, b;
char a, b;
};
static constexpr char kHexCharsLower[] = "0123456789abcdef";
@ -23,10 +23,9 @@ 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
}// namespace detail
}// namespace tile
#endif // _TILE_BASE_ENCODING_DETAIL_HEX_CHARS_H
#endif// _TILE_BASE_ENCODING_DETAIL_HEX_CHARS_H

View File

@ -6,48 +6,49 @@
namespace tile {
std::string EncodeHex(Slice from, bool uppercase) {
std::string result;
EncodeHex(from, &result, uppercase);
return result;
std::string
EncodeHex(Slice from, bool uppercase)
{
std::string result;
EncodeHex(from, &result, uppercase);
return result;
}
std::optional<std::string> DecodeHex(Slice from) {
std::string result;
if (!DecodeHex(from, &result)) {
return std::nullopt;
}
return result;
std::optional<std::string>
DecodeHex(Slice from)
{
std::string result;
if (!DecodeHex(from, &result)) { return std::nullopt; }
return result;
}
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);
to->append({pair.first, pair.second});
}
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);
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;
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; }
TILE_CHECK(v >= 0 && v <= 255);
to->push_back(v);
}
TILE_CHECK(v >= 0 && v <= 255);
to->push_back(v);
}
return true;
return true;
}
} // namespace tile
}// namespace tile

View File

@ -11,6 +11,6 @@ std::optional<std::string> DecodeHex(Slice from);
void EncodeHex(Slice from, std::string *to, bool uppercase = false);
bool DecodeHex(Slice from, std::string *to);
} // namespace tile
}// namespace tile
#endif // TILE_BASE_ENCODING_HEX_H
#endif// TILE_BASE_ENCODING_HEX_H

View File

@ -7,23 +7,25 @@
namespace tile {
const char Hex123456FF[] = "\x12\x34\x56\xFF";
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, 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) {
std::string random_str;
std::string encoded;
std::string decoded;
for (int i = 0; i != 1000; ++i) {
testing::RandomString(&random_str);
EncodeHex(random_str, &encoded);
ASSERT_TRUE(DecodeHex(encoded, &decoded));
EXPECT_EQ(random_str, decoded);
}
TEST(Hex, Random)
{
std::string random_str;
std::string encoded;
std::string decoded;
for (int i = 0; i != 1000; ++i) {
testing::RandomString(&random_str);
EncodeHex(random_str, &encoded);
ASSERT_TRUE(DecodeHex(encoded, &decoded));
EXPECT_EQ(random_str, decoded);
}
}
} // namespace tile
}// namespace tile

View File

@ -7,112 +7,107 @@ namespace tile {
// [0-9a-zA-Z]
namespace {
std::array<bool, 256> GenerateUnescapedCharBitmap(Slice unescaped_chars) {
std::array<bool, 256> result{};
for (auto &&e : unescaped_chars) {
result[e] = true;
}
std::array<bool, 256>
GenerateUnescapedCharBitmap(Slice unescaped_chars)
{
std::array<bool, 256> result{};
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;
result[i + 'a'] = true;
}
for (int i = 0; i != 26; ++i) {
result[i + 'A'] = true;
result[i + 'a'] = true;
}
return result;
return result;
}
const std::array<std::array<bool, 256>, 2> &
GetUnescapedCharBitmap(const PercentEncodingStyle &style) {
static const std::array<std::array<bool, 256>, 2> kUnescapedChars[] = {
/* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"),
GenerateUnescapedCharBitmap("_-!.*~'()")},
GetUnescapedCharBitmap(const PercentEncodingStyle &style)
{
static const std::array<std::array<bool, 256>, 2> kUnescapedChars[] = {
/* emca262 = */ {GenerateUnescapedCharBitmap("_-,;:!?.'()@*/&#+=~$"), GenerateUnescapedCharBitmap("_-!.*~'()") },
/* rfc3986 = */
{GenerateUnescapedCharBitmap("_-,;:!?.'()[]@*/&#+=~$"),
GenerateUnescapedCharBitmap("_-.~")
/* rfc3986 = */
{GenerateUnescapedCharBitmap("_-,;:!?.'()[]@*/&#+=~$"), GenerateUnescapedCharBitmap("_-.~")
},
/* rfc5987 = */
{GenerateUnescapedCharBitmap("!#$&+-.^_`|~"),
GenerateUnescapedCharBitmap("!#$&+-.^_`|~")}};
},
/* rfc5987 = */
{GenerateUnescapedCharBitmap("!#$&+-.^_`|~"), GenerateUnescapedCharBitmap("!#$&+-.^_`|~")}
};
return kUnescapedChars[static_cast<int>(style)];
return kUnescapedChars[static_cast<int>(style)];
}
} // namespace
}// 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 result;
EncodePercent(from, &result, options);
return result;
}
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)) {
std::string
EncodePercent(Slice from, const PercentEncodingOptions &options)
{
std::string result;
EncodePercent(from, &result, options);
return result;
}
return std::nullopt;
}
void EncodePercent(Slice from, std::string *to,
const PercentEncodingOptions &options) {
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;
}
}
to->clear();
to->reserve(from.size() + escape_char_count * 2);
for (auto &&e : from) {
if (TILE_UNLIKELY(unescaped[static_cast<std::uint8_t>(e)])) {
to->push_back(e);
} else {
auto hex_pair = detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e));
to->append({'%', hex_pair.first, hex_pair.second});
}
}
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; }
return std::nullopt;
}
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;
}
auto v = detail::AsciiCodeFromCharPair(*(iter + 1), *(iter + 2));
if (TILE_LIKELY(v != -1)) {
to->push_back(v);
iter += 3;
} else {
// invalid char
return false;
}
} else {
if (decode_plus_sign_as_whitespace && *iter == '+') {
to->push_back(' ');
++iter;
} else {
to->push_back(*iter++);
}
void
EncodePercent(Slice from, std::string *to, const PercentEncodingOptions &options)
{
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; }
}
to->clear();
to->reserve(from.size() + escape_char_count * 2);
for (auto &&e : from) {
if (TILE_UNLIKELY(unescaped[static_cast<std::uint8_t>(e)])) {
to->push_back(e);
} else {
auto hex_pair = detail::AsciiCodeToCharPair(static_cast<std::uint8_t>(e));
to->append({'%', hex_pair.first, hex_pair.second});
}
}
}
return true;
}
} // namespace tile
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; }
auto v = detail::AsciiCodeFromCharPair(*(iter + 1), *(iter + 2));
if (TILE_LIKELY(v != -1)) {
to->push_back(v);
iter += 3;
} else {
// invalid char
return false;
}
} else {
if (decode_plus_sign_as_whitespace && *iter == '+') {
to->push_back(' ');
++iter;
} else {
to->push_back(*iter++);
}
}
}
return true;
}
}// namespace tile

View File

@ -9,32 +9,28 @@
namespace tile {
enum class PercentEncodingStyle {
Ecma262 = 0,
Rfc3986 = 1,
Rfc5987 = 2,
Ecma262 = 0,
Rfc3986 = 1,
Rfc5987 = 2,
};
struct PercentEncodingOptions {
PercentEncodingStyle style = PercentEncodingStyle::Rfc3986;
bool escape_reserved = true;
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
}// namespace tile
#endif // TILE_BASE_ENCODING_PERCENT_H
#endif// TILE_BASE_ENCODING_PERCENT_H

View File

@ -6,103 +6,97 @@
namespace tile {
TEST(PercentEncoding, Emca262) {
// Shamelessly copied from:
//
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
Slice set1 = ";,/?:@&=+$"; // Reserved Characters
Slice set2 = "-_.!~*'()"; // Unescaped Characters
Slice set3 = "#"; // Number Sign
Slice set4 = "ABC abc 123"; // Alphanumeric Characters + Space
TEST(PercentEncoding, Emca262)
{
// Shamelessly copied from:
//
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
Slice set1 = ";,/?:@&=+$"; // Reserved Characters
Slice set2 = "-_.!~*'()"; // Unescaped Characters
Slice set3 = "#"; // Number Sign
Slice set4 = "ABC abc 123";// Alphanumeric Characters + Space
// Reserved chars are escaped.
const PercentEncodingOptions ecma262_reversed(PercentEncodingStyle::Ecma262,
true);
// Reserved chars are escaped.
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("-_.!~*'()", EncodePercent(set2, ecma262_reversed));
EXPECT_EQ("%23", EncodePercent(set3, ecma262_reversed));
EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, 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));
EXPECT_EQ(set1, DecodePercent("%3B%2C%2F%3F%3A%40%26%3D%2B%24"));
EXPECT_EQ(set2, DecodePercent("-_.!~*'()"));
EXPECT_EQ(set3, DecodePercent("%23"));
EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123"));
EXPECT_EQ(set1, DecodePercent("%3B%2C%2F%3F%3A%40%26%3D%2B%24"));
EXPECT_EQ(set2, DecodePercent("-_.!~*'()"));
EXPECT_EQ(set3, DecodePercent("%23"));
EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123"));
// Reserved chars are kept.
const PercentEncodingOptions ecma262(PercentEncodingStyle::Ecma262, false);
EXPECT_EQ(";,/?:@&=+$", EncodePercent(set1, ecma262));
EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262));
EXPECT_EQ("#", EncodePercent(set3, ecma262));
EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, ecma262));
// Reserved chars are kept.
const PercentEncodingOptions ecma262(PercentEncodingStyle::Ecma262, false);
EXPECT_EQ(";,/?:@&=+$", EncodePercent(set1, ecma262));
EXPECT_EQ("-_.!~*'()", EncodePercent(set2, ecma262));
EXPECT_EQ("#", EncodePercent(set3, ecma262));
EXPECT_EQ("ABC%20abc%20123", EncodePercent(set4, ecma262));
EXPECT_EQ(set1, DecodePercent(";,/?:@&=+$"));
EXPECT_EQ(set2, DecodePercent("-_.!~*'()"));
EXPECT_EQ(set3, DecodePercent("#"));
EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123"));
EXPECT_EQ(set1, DecodePercent(";,/?:@&=+$"));
EXPECT_EQ(set2, DecodePercent("-_.!~*'()"));
EXPECT_EQ(set3, DecodePercent("#"));
EXPECT_EQ(set4, DecodePercent("ABC%20abc%20123"));
}
TEST(PercentEncoding, Rfc3986) {
Slice str = "_-,;:!?.'()[]@*/&#+=~$ABC abc 123";
TEST(PercentEncoding, Rfc3986)
{
Slice str = "_-,;:!?.'()[]@*/&#+=~$ABC abc 123";
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%"
"20123",
EncodePercent(str, rfc3986_reversed));
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"));
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%"
"20123",
EncodePercent(str, rfc3986_reversed));
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) {
// `encodeRFC5987ValueChars from MDN does not seems quite right (in that it
// does escape `#` `$` ..., which is not required.):
//
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
//
// auto encode_rfc5987_value_chars = [](Slice str) {
// auto result =
// EncodePercent(str, PercentEncodingOptions{.style =
// PercentEncodingStyle::Ecma262});
// result = Replace(result, "'", "%" + EncodeHex("'"));
// result = Replace(result, "(", "%" + EncodeHex("("));
// result = Replace(result, ")", "%" + EncodeHex(")"));
// result = Replace(result, "*", "%" + EncodeHex("*"));
// result = Replace(result, "%7C", "|");
// result = Replace(result, "%60", "`");
// result = Replace(result, "%5E", "^");
// return result;
// };
//
// EXPECT_EQ(
// encode_rfc5987_value_chars(str),
// EncodePercent(str, PercentEncodingOptions{.style =
// PercentEncodingStyle::Rfc5987}));
TEST(PercentEncoding, Rfc5987)
{
// `encodeRFC5987ValueChars from MDN does not seems quite right (in that it
// does escape `#` `$` ..., which is not required.):
//
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
//
// auto encode_rfc5987_value_chars = [](Slice str) {
// auto result =
// EncodePercent(str, PercentEncodingOptions{.style =
// PercentEncodingStyle::Ecma262});
// result = Replace(result, "'", "%" + EncodeHex("'"));
// result = Replace(result, "(", "%" + EncodeHex("("));
// result = Replace(result, ")", "%" + EncodeHex(")"));
// result = Replace(result, "*", "%" + EncodeHex("*"));
// result = Replace(result, "%7C", "|");
// result = Replace(result, "%60", "`");
// result = Replace(result, "%5E", "^");
// return result;
// };
//
// EXPECT_EQ(
// encode_rfc5987_value_chars(str),
// EncodePercent(str, PercentEncodingOptions{.style =
// PercentEncodingStyle::Rfc5987}));
const PercentEncodingOptions rfc5987(PercentEncodingStyle::Rfc5987, false);
const PercentEncodingOptions rfc5987_reversed(PercentEncodingStyle::Rfc5987,
true);
const PercentEncodingOptions rfc5987(PercentEncodingStyle::Rfc5987, false);
const PercentEncodingOptions rfc5987_reversed(PercentEncodingStyle::Rfc5987, true);
Slice str = "!123'-!#$&()*,./:;?@[]^_`|~+=ABC abc";
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) {
EXPECT_EQ("a+b", DecodePercent("a+b"));
EXPECT_EQ("a b", DecodePercent("a+b", true));
TEST(PercentEncoding, DecodePlusSignAsWhitespace)
{
EXPECT_EQ("a+b", DecodePercent("a+b"));
EXPECT_EQ("a b", DecodePercent("a+b", true));
}
} // namespace tile
}// namespace tile

View File

@ -7,77 +7,90 @@
namespace tile {
void Benchmark_Base64Encode(benchmark::State &state) {
std::string random_str;
std::string out;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
state.ResumeTiming();
void
Benchmark_Base64Encode(benchmark::State &state)
{
std::string random_str;
std::string out;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
state.ResumeTiming();
EncodeBase64(random_str, &out);
}
}
void Benchmark_Base64Decode(benchmark::State &state) {
std::string random_str;
std::string base64;
std::string plain_text;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
EncodeBase64(random_str, &base64);
state.ResumeTiming();
DecodeBase64(base64, &plain_text);
}
EncodeBase64(random_str, &out);
}
}
void Benchmark_PercentEncode(benchmark::State &state) {
std::string random_str;
std::string out;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
state.ResumeTiming();
EncodeBase64(random_str, &out);
}
void
Benchmark_Base64Decode(benchmark::State &state)
{
std::string random_str;
std::string base64;
std::string plain_text;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
EncodeBase64(random_str, &base64);
state.ResumeTiming();
DecodeBase64(base64, &plain_text);
}
}
void Benchmark_PercentDecode(benchmark::State &state) {
std::string random_str;
std::string percent;
std::string plain_text;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
EncodePercent(random_str, &percent);
state.ResumeTiming();
DecodePercent(percent, &plain_text);
}
void
Benchmark_PercentEncode(benchmark::State &state)
{
std::string random_str;
std::string out;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
state.ResumeTiming();
EncodeBase64(random_str, &out);
}
}
void Benchmark_HexEncode(benchmark::State &state) {
std::string random_str;
std::string percent;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
state.ResumeTiming();
EncodeHex(random_str, &percent);
}
void
Benchmark_PercentDecode(benchmark::State &state)
{
std::string random_str;
std::string percent;
std::string plain_text;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
EncodePercent(random_str, &percent);
state.ResumeTiming();
DecodePercent(percent, &plain_text);
}
}
void Benchmark_HexDecode(benchmark::State &state) {
std::string random_str;
std::string percent;
std::string plain_text;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
EncodeHex(random_str, &percent);
state.ResumeTiming();
DecodeHex(percent, &plain_text);
}
void
Benchmark_HexEncode(benchmark::State &state)
{
std::string random_str;
std::string percent;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
state.ResumeTiming();
EncodeHex(random_str, &percent);
}
}
void
Benchmark_HexDecode(benchmark::State &state)
{
std::string random_str;
std::string percent;
std::string plain_text;
while (state.KeepRunning()) {
state.PauseTiming();
testing::RandomString(&random_str);
EncodeHex(random_str, &percent);
state.ResumeTiming();
DecodeHex(percent, &plain_text);
}
}
BENCHMARK(Benchmark_Base64Encode);
@ -86,4 +99,4 @@ BENCHMARK(Benchmark_PercentEncode);
BENCHMARK(Benchmark_PercentDecode);
BENCHMARK(Benchmark_HexEncode);
BENCHMARK(Benchmark_HexDecode);
} // namespace tile
}// namespace tile

View File

@ -6,35 +6,33 @@
#include "tile/base/internal/meta.h"
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 {
return static_cast<typename std::underlying_type<T>::type>(v);
template<class T, class = enable_if_t<std::is_enum<T>::value>>
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)); \
}
#define TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, Op) \
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; }
#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)); \
}
#define TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, Op) \
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; }
#define TILE_DEFINE_ENUM_BITMASK_OPS(Type) \
TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, |) \
TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, &) \
TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, ^) \
TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(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); \
}
#define TILE_DEFINE_ENUM_BITMASK_OPS(Type) \
TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, |) \
TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, &) \
TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, ^) \
TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(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); }
} // namespace tile
}// namespace tile
#endif // _TILE_BASE_ENUM_H
#endif// _TILE_BASE_ENUM_H

View File

@ -12,58 +12,69 @@ namespace tile {
// RAII wrapper ptr
class ErasedPtr final {
public:
using Deleter = void (*)(void *);
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); }) {}
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;
}
using Deleter = void (*)(void *);
ErasedPtr &operator=(ErasedPtr &&ptr) noexcept {
if (TILE_LIKELY(this != &ptr)) {
Reset();
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); })
{}
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; }
ErasedPtr &operator=(ErasedPtr &&ptr) noexcept
{
if (TILE_LIKELY(this != &ptr)) { Reset(); }
return *this;
}
return *this;
}
ErasedPtr(const ErasedPtr &) = delete;
ErasedPtr &operator=(const ErasedPtr &) = delete;
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 {
return static_cast<T *>(ptr_);
}
constexpr explicit operator bool() const noexcept { return !!ptr_; }
void Reset(std::nullptr_t = nullptr) noexcept {
if (ptr_) {
deleter_(ptr_);
ptr_ = nullptr;
constexpr void *Get() const noexcept { return ptr_; }
template<typename T>
T *UncheckedGet() const noexcept
{
return static_cast<T *>(ptr_);
}
}
TILE_NODISCARD void *Leak() noexcept {
void *rc = nullptr;
std::swap(rc, ptr_);
return rc;
}
constexpr Deleter GetDeleter() const noexcept { return deleter_; }
constexpr explicit operator bool() const noexcept { return !!ptr_; }
void Reset(std::nullptr_t = nullptr) noexcept
{
if (ptr_) {
deleter_(ptr_);
ptr_ = nullptr;
}
}
TILE_NODISCARD void *Leak() noexcept
{
void *rc = nullptr;
std::swap(rc, ptr_);
return rc;
}
constexpr Deleter GetDeleter() const noexcept { return deleter_; }
private:
void *ptr_;
Deleter deleter_;
void *ptr_;
Deleter deleter_;
};
} // namespace tile
}// namespace tile
#endif // _TILE_BASE_ERASED_PTR_H
#endif// _TILE_BASE_ERASED_PTR_H

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -15,179 +15,188 @@
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> {
return t;
template<typename T>
auto
ToJsonValue(const T &t) -> enable_if_t<std::is_same<T, Json::Value>::value, Json::Value>
{
return t;
}
template <typename T>
auto ToJsonValue(const T &t)
-> enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
Json::Value> {
return Json::Value(static_cast<Json::UInt64>(t));
template<typename T>
auto
ToJsonValue(const T &t) -> enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, Json::Value>
{
return Json::Value(static_cast<Json::UInt64>(t));
}
template <typename T>
auto ToJsonValue(const T &t)
-> enable_if_t<std::is_integral<T>::value && !std::is_unsigned<T>::value,
Json::Value> {
return Json::Value(static_cast<Json::Int64>(t));
template<typename T>
auto
ToJsonValue(const T &t) -> enable_if_t<std::is_integral<T>::value && !std::is_unsigned<T>::value, Json::Value>
{
return Json::Value(static_cast<Json::Int64>(t));
}
template <typename T>
auto ToJsonValue(const T &t)
-> enable_if_t<std::is_floating_point<T>::value ||
std::is_same<T, std::string>::value ||
std::is_same<T, const char *>::value ||
std::is_same<T, bool>::value,
Json::Value> {
return Json::Value(t);
template<typename T>
auto
ToJsonValue(const T &t) -> enable_if_t<std::is_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> {
return Json::Value(format_as(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>
{
return Json::Value(format_as(t));
}
template <class T> Json::Value ToJsonValue(const std::atomic<T> &v) {
return ToJsonValue(v.load(std::memory_order_relaxed));
template<class T>
Json::Value
ToJsonValue(const std::atomic<T> &v)
{
return ToJsonValue(v.load(std::memory_order_relaxed));
}
} // namespace exposed_var
}// namespace exposed_var
class ExposedVarDynamicTree;
class ExposedVarGroup {
public:
using Handle = Deferred;
using Handle = Deferred;
Handle Add(Slice rel_path, std::function<Json::Value()> value);
Handle Add(Slice rel_path, ExposedVarDynamicTree *dyanmic_tree);
static ExposedVarGroup *FindOrCreate(Slice abs_path);
static std::optional<Json::Value> TryGet(Slice abs_path);
Handle Add(Slice rel_path, std::function<Json::Value()> value);
Handle Add(Slice rel_path, ExposedVarDynamicTree *dyanmic_tree);
static ExposedVarGroup *FindOrCreate(Slice abs_path);
static std::optional<Json::Value> TryGet(Slice abs_path);
private:
using Getter = std::function<std::optional<Json::Value>(Slice)>;
using Getter = std::function<std::optional<Json::Value>(Slice)>;
explicit ExposedVarGroup(std::string abs_path);
const std::string &AbsolutePath() const;
explicit ExposedVarGroup(std::string abs_path);
const std::string &AbsolutePath() const;
ExposedVarGroup *FindLowest(Slice rel_path, Slice *left);
ExposedVarGroup *CreateUpto(Slice rel_path);
Handle AddDirect(Slice name, Getter getter);
Json::Value Dump() const;
static ExposedVarGroup *Root();
ExposedVarGroup *FindLowest(Slice rel_path, Slice *left);
ExposedVarGroup *CreateUpto(Slice rel_path);
Handle AddDirect(Slice name, Getter getter);
Json::Value Dump() const;
static ExposedVarGroup *Root();
private:
std::string abs_path_;
mutable std::mutex lock_;
std::string abs_path_;
mutable std::mutex lock_;
std::unordered_map<std::string, std::unique_ptr<ExposedVarGroup>> nodes_;
std::unordered_map<std::string, Getter> leaves_;
std::unordered_map<std::string, std::unique_ptr<ExposedVarGroup>> nodes_;
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)) {
LinkToParent(rel_path, parent);
}
template<typename U>
ExposedVar(Slice rel_path, U &&initial_value, ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"))
: obj_(std::forward<U>(initial_value))
{
LinkToParent(rel_path, parent);
}
T *operator->() noexcept { return &obj_; }
T &operator*() noexcept { return obj_; }
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:
T obj_{};
ExposedVarGroup::Handle handle_;
T obj_{};
ExposedVarGroup::Handle handle_;
};
template <typename T> class ExposedVarDynamic {
template<typename T>
class ExposedVarDynamic {
public:
ExposedVarDynamic(
Slice rel_path, std::function<T()> getter,
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"))
: getter_(std::move(getter)) {
handle_ = parent->Add(
rel_path, [this] { return exposed_var::ToJsonValue(getter_()); });
}
ExposedVarDynamic(Slice rel_path,
std::function<T()> getter,
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"))
: getter_(std::move(getter))
{
handle_ = parent->Add(rel_path, [this] { return exposed_var::ToJsonValue(getter_()); });
}
private:
std::function<T()> getter_;
ExposedVarGroup::Handle handle_;
std::function<T()> getter_;
ExposedVarGroup::Handle handle_;
};
class ExposedVarDynamicTree {
public:
ExposedVarDynamicTree(
Slice rel_path, std::function<Json::Value()> getter,
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"));
ExposedVarDynamicTree(Slice rel_path,
std::function<Json::Value()> getter,
ExposedVarGroup *parent = ExposedVarGroup::FindOrCreate("/"));
std::optional<Json::Value> TryGet(Slice rel_path) const;
std::optional<Json::Value> TryGet(Slice rel_path) const;
private:
std::function<Json::Value()> getter_;
ExposedVarGroup::Handle handle_;
std::function<Json::Value()> getter_;
ExposedVarGroup::Handle handle_;
};
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
}// namespace detail
template <typename T, typename F = detail::IdentityTime<T>>
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 result;
std::unordered_map<std::string, T> m = {{"1s", 1}};
for (auto &&item : m) {
Json::Value ToJsonValue(const T &v)
{
Json::Value result;
std::unordered_map<std::string, T> m = {
{"1s", 1}
};
for (auto &&item : m) {}
}
}
void LinkToParent(Slice rel_path, ExposedVarGroup *parent) {
handle_ = parent->Add(rel_path, [this] { return ToJsonValue(obj_); });
}
void LinkToParent(Slice rel_path, ExposedVarGroup *parent)
{
handle_ = parent->Add(rel_path, [this] { return ToJsonValue(obj_); });
}
private:
T obj_{};
ExposedVarGroup::Handle handle_;
T obj_{};
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
}// namespace tile
#endif // TILE_BASE_EXPOSED_VAR_H
#endif// TILE_BASE_EXPOSED_VAR_H

View File

@ -4,14 +4,19 @@
#include "json/json.h"
namespace tile {
ExposedVarGroup *GetFancyGroup() {
return ExposedVarGroup::FindOrCreate("/a/b");
ExposedVarGroup *
GetFancyGroup()
{
return ExposedVarGroup::FindOrCreate("/a/b");
}
Json::Value GetTree() {
Json::Value jsv;
jsv["dir"]["sub-dir"]["key"] = 5;
jsv["key"] = "6";
return jsv;
Json::Value
GetTree()
{
Json::Value jsv;
jsv["dir"]["sub-dir"]["key"] = 5;
jsv["key"] = "6";
return jsv;
}
ExposedVar<int> v1("v1", 5);
@ -21,18 +26,18 @@ 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) {
auto opt = ExposedVarGroup::TryGet("/");
ASSERT_TRUE(opt);
auto &&jsv = *opt;
ASSERT_EQ(5, jsv["v1"].asInt());
*v1 = 6;
jsv = *ExposedVarGroup::TryGet("/");
ASSERT_EQ(6, jsv["v1"].asInt());
*v1 = 5;
TEST(ExposedVar, Mutate)
{
auto opt = ExposedVarGroup::TryGet("/");
ASSERT_TRUE(opt);
auto &&jsv = *opt;
ASSERT_EQ(5, jsv["v1"].asInt());
*v1 = 6;
jsv = *ExposedVarGroup::TryGet("/");
ASSERT_EQ(6, jsv["v1"].asInt());
*v1 = 5;
}
} // namespace tile
}// namespace tile

View File

@ -22,6 +22,6 @@ using future::Split;
using future::WhenAll;
using future::WhenAny;
} // namespace tile
}// namespace tile
#endif // TILE_BASE_FUTURE_H
#endif// TILE_BASE_FUTURE_H

View File

@ -10,54 +10,63 @@
namespace tile {
namespace future {
template <typename... Ts>
struct are_rvalue_refs
: internal::conjunction<std::is_rvalue_reference<Ts>...> {};
template<typename... 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...>;
using type = To<Ts...>;
};
template <typename T, template <typename...> class To>
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, "");
static_assert(is_future<Future<char *>>::value, "");
static_assert(is_future<Future<char *, int>>::value, "");
template <typename... Ts>
template<typename... Ts>
struct is_futures : internal::conjunction<is_future<Ts>...> {};
template <typename... Ts> struct futurize {
using type = Future<Ts...>;
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,41 +75,43 @@ 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 {
using type = rebind_t<types_cat_t<rebind_t<Ts, Types>...>, Future>;
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>
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
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_BASIC_H
#endif// TILE_BASE_FUTURE_BASIC_H

View File

@ -13,159 +13,175 @@ namespace tile {
namespace future {
namespace detail {
// T = Boxed<...>
template <typename T> T RetrieveBoxed();
} // namespace detail
template<typename T>
T RetrieveBoxed();
}// namespace detail
struct box_values_t {
explicit box_values_t() = default;
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...>");
using value_type = std::tuple<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 lvalue_ref = internal::add_lvalue_reference_t<unboxed_type_t<Ts...>>;
using rvalue_ref = internal::add_rvalue_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>>
explicit Boxed(box_values_t, Us &&...imms);
template <typename... Us, typename = enable_if_t<std::is_constructible<
value_type, std::tuple<Us &&...>>::value>>
Boxed(Boxed<Us...> boxed);
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>>
Boxed(Boxed<Us...> boxed);
lvalue_ref Get() &;
rvalue_ref Get() &&;
// internal::add_lvalue_reference_t<unboxed_type_t<Ts...>> Get() &;
// internal::add_rvalue_reference_t<unboxed_type_t<Ts...>> Get() &&;
lvalue_ref Get() &;
rvalue_ref Get() &&;
// internal::add_lvalue_reference_t<unboxed_type_t<Ts...>> Get() &;
// internal::add_rvalue_reference_t<unboxed_type_t<Ts...>> Get() &&;
value_type &GetRaw() &;
value_type &&GetRaw() &&;
value_type &GetRaw() &;
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;
static constexpr std::size_t kEmpty = 0;
static constexpr std::size_t kValue = 1;
Boxed() = default;
Boxed() = default;
template <typename... Us>
enable_if_t<sizeof...(Us) == 0, lvalue_ref> GetImpl() & {
return (void)GetRaw();
}
template<typename... Us>
enable_if_t<sizeof...(Us) == 0, lvalue_ref> GetImpl() &
{
return (void) GetRaw();
}
template <typename... Us>
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() &
{
return std::get<0>(GetRaw());
}
template <typename... Us>
enable_if_t<(sizeof...(Us) > 1), lvalue_ref> GetImpl() & {
return GetRaw();
}
template<typename... Us>
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() & {
return Get();
}
template <typename... Us>
enable_if_t<
!std::is_void<decltype(std::declval<Boxed<Us...>>().Get())>::value,
rvalue_ref>
GetMoveImpl() & {
return std::move(Get());
}
template<typename... Us>
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() &
{
return std::move(Get());
}
private:
std::variant<mpark::monostate, value_type> holding_;
std::variant<mpark::monostate, value_type> holding_;
};
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(); }
} // namespace detail
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)...) {}
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)...)
{}
template <typename... Ts>
template <typename... Us, typename>
Boxed<Ts...>::Boxed(Boxed<Us...> boxed) {
holding_.template emplace<kValue>(
mpark::get<kValue>(std::move(boxed).holding_));
template<typename... Ts>
template<typename... Us, typename>
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() & {
TILE_CHECK_NE(holding_.index(), kEmpty);
return GetImpl<Ts...>();
}
template <class... Ts>
typename Boxed<Ts...>::rvalue_ref Boxed<Ts...>::Get() && {
return GetMoveImpl<Ts...>();
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...>::value_type &Boxed<Ts...>::GetRaw() & {
TILE_CHECK_NE(holding_.index(), kEmpty);
return mpark::get<kValue>(holding_);
template<class... Ts>
typename Boxed<Ts...>::rvalue_ref
Boxed<Ts...>::Get() &&
{
return GetMoveImpl<Ts...>();
}
template <class... Ts>
typename Boxed<Ts...>::value_type &&Boxed<Ts...>::GetRaw() && {
return std::move(GetRaw());
template<class... Ts>
typename Boxed<Ts...>::value_type &
Boxed<Ts...>::GetRaw() &
{
TILE_CHECK_NE(holding_.index(), kEmpty);
return mpark::get<kValue>(holding_);
}
} // namespace future
} // namespace tile
template<class... Ts>
typename Boxed<Ts...>::value_type &&
Boxed<Ts...>::GetRaw() &&
{
return std::move(GetRaw());
}
}// namespace future
}// namespace tile
namespace tile {
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...>> {
return apply(std::forward<F>(f), std::move(boxed.GetRaw()));
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...>>
{
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...>>> {
return invoke(f, std::move(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...>>>
{
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>
template<typename F, typename... Ts>
using call_by_boxed_t = typename call_by_boxed<F, Ts...>::type;
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_BOXED_H
#endif// TILE_BASE_FUTURE_BOXED_H

View File

@ -6,61 +6,64 @@ namespace tile {
namespace future {
namespace detail {
TEST(Boxed, Construct) {
Boxed<> box1(box_values);
static_assert(std::is_same<decltype(box1.Get()), void>::value, "");
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);
Boxed<int> box2(box_values, 1);
EXPECT_EQ(box2.Get(), 1);
Boxed<int> box3(box2);
EXPECT_EQ(box3.Get(), 1);
EXPECT_EQ(box3.GetRaw(), std::make_tuple(1));
Boxed<int> box3(box2);
EXPECT_EQ(box3.Get(), 1);
EXPECT_EQ(box3.GetRaw(), std::make_tuple(1));
Boxed<int, char> box4(box_values, 1, 'a');
EXPECT_EQ(box4.Get(), std::make_tuple(1, 'a'));
Boxed<int, char> box4(box_values, 1, 'a');
EXPECT_EQ(box4.Get(), std::make_tuple(1, 'a'));
}
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() {};
static void Test(int) {};
static void Test(int, int) {};
void MTest() {};
void MTest(int) {};
void MTest(int, int) {};
} at;
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);
// internal::apply(f1, t1);
// internal::apply(f2, t2);
// internal::apply(f3, t3);
// internal::apply(&ApplyTest::Test, t1);
class ApplyTest {
public:
static void Test() {};
static void Test(int) {};
static void Test(int, int) {};
void MTest() {};
void MTest(int) {};
void MTest(int, int) {};
} at;
// internal::apply(f1, t1);
// internal::apply(f2, t2);
// internal::apply(f3, t3);
// internal::apply(&ApplyTest::Test, t1);
}
template <typename... Ts>
void Consume(std::function<void(Boxed<Ts...> &&) noexcept> &&action,
Boxed<Ts...> &&val) {
action(std::move(val));
template<typename... Ts>
void
Consume(std::function<void(Boxed<Ts...> &&) noexcept> &&action, Boxed<Ts...> &&val)
{
action(std::move(val));
}
TEST(Boxed, Consume) {
Boxed<int> box(box_values, 1);
std::function<void(Boxed<int> &&) noexcept> l1 = [](Boxed<int> &&) noexcept {
};
auto l2 = [](Boxed<int> &&) noexcept {};
Consume(std::move(l1), std::move(box));
TEST(Boxed, Consume)
{
Boxed<int> box(box_values, 1);
std::function<void(Boxed<int> &&) noexcept> l1 = [](Boxed<int> &&) noexcept {};
auto l2 = [](Boxed<int> &&) noexcept {};
Consume(std::move(l1), std::move(box));
}
} // namespace detail
} // namespace future
} // namespace tile
}// namespace detail
}// namespace future
}// namespace tile

View File

@ -12,88 +12,87 @@
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>;
using value_type = Boxed<Ts...>;
using action_type = std::function<void(value_type &&) noexcept>;
explicit Core(Executor executor) : executor_(std::move(executor)) {}
explicit Core(Executor executor) : executor_(std::move(executor)) {}
void SetBoxed(value_type &&value) noexcept;
void ChainAction(action_type action) noexcept;
Executor GetExecutor() const noexcept;
void SetBoxed(value_type &&value) noexcept;
void ChainAction(action_type action) noexcept;
Executor GetExecutor() const noexcept;
private:
struct WaitingForSingleObject {
WaitingForSingleObject() {}
WaitingForSingleObject(action_type &&action)
: on_satisfied(std::move(action)) {}
action_type on_satisfied;
};
struct WaitingForSingleObject {
WaitingForSingleObject() {}
struct Satisfied {
Satisfied(value_type &&v, bool e)
: value(std::move(v)), ever_called_continuation(e) {}
WaitingForSingleObject(action_type &&action) : on_satisfied(std::move(action)) {}
value_type value;
bool ever_called_continuation{false};
};
action_type on_satisfied;
};
struct Satisfied {
Satisfied(value_type &&v, bool e) : value(std::move(v)), ever_called_continuation(e) {}
value_type value;
bool ever_called_continuation{false};
};
private:
// std::mutex lock_;
Mutex lock_;
std::variant<WaitingForSingleObject, Satisfied> state_;
Executor executor_;
// std::mutex lock_;
Mutex lock_;
std::variant<WaitingForSingleObject, Satisfied> state_;
Executor executor_;
};
template <typename... Ts>
void Core<Ts...>::SetBoxed(value_type &&value) noexcept {
UniqueLock<Mutex> lk(lock_);
action_type action =
std::move(mpark::get<WaitingForSingleObject>(state_).on_satisfied);
template<typename... Ts>
void
Core<Ts...>::SetBoxed(value_type &&value) noexcept
{
UniqueLock<Mutex> lk(lock_);
action_type action = std::move(mpark::get<WaitingForSingleObject>(state_).on_satisfied);
state_.template emplace<Satisfied>(Satisfied{std::move(value), !!action});
state_.template emplace<Satisfied>(Satisfied{std::move(value), !!action});
if (action) {
lk.Unlock();
if (action) {
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_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()); });
}
}
template <typename... Ts>
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)});
} 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.");
template<typename... Ts>
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)});
} 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.");
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());
});
}
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()); });
}
}
template <typename... Ts>
inline Executor Core<Ts...>::GetExecutor() const noexcept {
return executor_;
template<typename... Ts>
inline Executor
Core<Ts...>::GetExecutor() const noexcept
{
return executor_;
}
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_CORE_H
#endif// TILE_BASE_FUTURE_CORE_H

View File

@ -15,91 +15,96 @@ namespace tile {
namespace future {
class Executor {
public:
constexpr Executor() = default;
constexpr Executor() = default;
// Copy & move
Executor(const Executor &other) : impl_(other.impl_->Clone()) {}
Executor &operator=(const Executor &e) {
impl_ = e.impl_->Clone();
return *this;
}
Executor(Executor &&other) = default;
Executor &operator=(Executor &&) = default;
// Copy & move
Executor(const Executor &other) : impl_(other.impl_->Clone()) {}
// 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) {
impl_ = EraseExecutorType(std::forward<T>(executor));
}
Executor &operator=(const Executor &e)
{
impl_ = e.impl_->Clone();
return *this;
}
template <typename T> void Execute(T &&job) {
job();
// impl_->Execute(std::forward<T>(job));
}
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)
{
impl_ = EraseExecutorType(std::forward<T>(executor));
}
template<typename T>
void Execute(T &&job)
{
job();
// impl_->Execute(std::forward<T>(job));
}
private:
class ConcreteExecutor {
public:
virtual ~ConcreteExecutor() = default;
virtual void Execute(std::function<void()> job) = 0;
virtual std::unique_ptr<ConcreteExecutor> Clone() = 0;
};
class ConcreteExecutor {
public:
virtual ~ConcreteExecutor() = default;
virtual void Execute(std::function<void()> job) = 0;
virtual std::unique_ptr<ConcreteExecutor> Clone() = 0;
};
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 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))
{}
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_); }
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> 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> impl_;
std::unique_ptr<ConcreteExecutor> impl_;
};
class InlineExecutor {
public:
void Execute(std::function<void()> job) { job(); }
void Execute(std::function<void()> job) { job(); }
};
namespace detail {
inline std::shared_ptr<Executor> &DefaultExecutorPtr() {
static std::shared_ptr<Executor> default_executor =
std::make_shared<Executor>(InlineExecutor());
return default_executor;
inline std::shared_ptr<Executor> &
DefaultExecutorPtr()
{
static std::shared_ptr<Executor> default_executor = std::make_shared<Executor>(InlineExecutor());
return default_executor;
}
} // namespace detail
}// 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) {
auto ptr = std::make_shared<Executor>(std::move(exec));
return *std::atomic_exchange_explicit(&detail::DefaultExecutorPtr(), ptr,
std::memory_order_release);
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);
}
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_EXECUTOR_H
#endif// TILE_BASE_FUTURE_EXECUTOR_H

View File

@ -8,124 +8,122 @@
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())) {
core_->SetBoxed(Boxed<Ts...>(box_values, std::forward<Us>(imms)...));
template<typename... Ts>
template<typename... Us, typename>
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>
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_->SetBoxed(Boxed<Ts...>(box_values, std::move(values)));
: 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())) {
static_assert(sizeof...(Ts) == 1, "");
core_->SetBoxed(Boxed<Ts...>(box_values, std::forward<U>(value)));
template<class... Ts>
template<class U, class>
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) {
Promise<Ts...> p;
template<class... Ts>
template<class... Us, class>
Future<Ts...>::Future(Future<Us...> &&future)
{
Promise<Ts...> p;
// Here we "steal" `p.GetFuture()`'s core and "install" it into ourself,
// thus once `p` is satisfied, we're satisfied as well.
core_ = p.GetFuture().core_;
auto moved_promise = MakeMoveOnCopy(std::move(p));
std::move(future).Then([moved_promise](Boxed<Us...> boxed) mutable noexcept {
moved_promise->SetBoxed(std::move(boxed));
});
// Here we "steal" `p.GetFuture()`'s core and "install" it into ourself,
// thus once `p` is satisfied, we're satisfied as well.
core_ = p.GetFuture().core_;
auto moved_promise = MakeMoveOnCopy(std::move(p));
std::move(future).Then([moved_promise](Boxed<Us...> boxed) mutable noexcept {
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<>> {
using NewFuture = futurize_t<R>;
as_promise_t<NewFuture> promise(core_->GetExecutor());
auto rc = promise.GetFuture();
template<typename... Ts>
template<typename F, typename R>
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());
auto moved_promise = MakeMoveOnCopy(promise);
auto moved_fun = MakeMoveOnCopy(std::forward<F>(fun));
auto rc = promise.GetFuture();
std::function<void(Boxed<Ts...> &&) noexcept> cont =
[moved_promise, moved_fun](Boxed<Ts...> &&value) mutable noexcept {
CallByBoxed(moved_fun.Move(), std::move(value));
auto moved_promise = MakeMoveOnCopy(promise);
auto moved_fun = MakeMoveOnCopy(std::forward<F>(fun));
moved_promise->SetBoxed(Boxed<>(box_values));
};
std::function<void(Boxed<Ts...> &&) noexcept> cont =
[moved_promise, moved_fun](Boxed<Ts...> &&value) mutable noexcept {
CallByBoxed(moved_fun.Move(), std::move(value));
core_->ChainAction(std::move(cont));
moved_promise->SetBoxed(Boxed<>(box_values));
};
return rc;
core_->ChainAction(std::move(cont));
return rc;
}
// 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>> {
using NewFuture = futurize_t<R>;
as_promise_t<NewFuture> promise(core_->GetExecutor());
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>>
{
using NewFuture = futurize_t<R>;
as_promise_t<NewFuture> promise(core_->GetExecutor());
auto rc = promise.GetFuture();
auto rc = promise.GetFuture();
auto moved_promise = MakeMoveOnCopy(promise);
auto moved_fun = MakeMoveOnCopy(std::forward<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 future = CallByBoxed(moved_fun.Move(), std::move(value));
// R is Future<Us...>
// build a lambda to set the value of the promise
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,
"");
moved_promise->SetBoxed(std::move(nested_v));
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));
});
};
};
core_->ChainAction(std::move(cont));
core_->ChainAction(std::move(cont));
return rc;
return rc;
}
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>> {
using NewFuture = futurize_t<R>;
as_promise_t<NewFuture> promise(core_->GetExecutor());
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>>
{
using NewFuture = futurize_t<R>;
as_promise_t<NewFuture> promise(core_->GetExecutor());
auto rc = promise.GetFuture();
auto rc = promise.GetFuture();
auto moved_promise = MakeMoveOnCopy(promise);
auto moved_fun = MakeMoveOnCopy(std::forward<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 {
as_boxed_t<NewFuture> boxed_value(
box_values, CallByBoxed(moved_fun.Move(), std::move(value)));
moved_promise->SetBoxed(std::move(boxed_value));
};
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)));
moved_promise->SetBoxed(std::move(boxed_value));
};
core_->ChainAction(std::move(cont));
return rc;
core_->ChainAction(std::move(cont));
return rc;
}
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_FUTURE_IMPL_H
#endif// TILE_BASE_FUTURE_FUTURE_IMPL_H

View File

@ -10,75 +10,70 @@
namespace tile {
namespace future {
struct futurize_values_t {
explicit futurize_values_t() = default;
explicit futurize_values_t() = default;
};
struct futurize_tuple_t {
explicit futurize_tuple_t() = default;
explicit futurize_tuple_t() = default;
};
constexpr auto futurize_values = futurize_values_t();
constexpr auto futurize_tuple = futurize_tuple_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>>
explicit Future(futurize_values_t, Us &&...imms);
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>>
explicit Future(futurize_values_t, Us &&...imms);
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... 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...>,
std::common_type<void>>::type,
U>::value &&
!is_future<U>::value>>
Future(U &&value);
template<
typename U,
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>>
Future(U &&value);
template <typename... Us, typename = enable_if_t<std::is_constructible<
Boxed<Ts...>, Boxed<Us...>>::value>>
Future(Future<Us...> &&future);
template<typename... Us, typename = enable_if_t<std::is_constructible<Boxed<Ts...>, Boxed<Us...>>::value>>
Future(Future<Us...> &&future);
Future() = default;
Future() = default;
Future(const Future &) = delete;
Future &operator=(const Future &) = delete;
Future(const Future &) = delete;
Future &operator=(const Future &) = delete;
Future(Future &&) = default;
Future &operator=(Future &&) = default;
Future(Future &&) = default;
Future &operator=(Future &&) = default;
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.");
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.");
template <typename F, typename R = call_by_boxed_t<F, Ts...>>
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, 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>>;
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>>;
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>>;
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>>;
private:
friend class Promise<Ts...>;
template <typename... Us> friend class Future;
friend class Promise<Ts...>;
template<typename... Us>
friend class Future;
explicit Future(std::shared_ptr<Core<Ts...>> core) : core_(std::move(core)) {}
std::shared_ptr<Core<Ts...>> core_;
explicit Future(std::shared_ptr<Core<Ts...>> core) : core_(std::move(core)) {}
std::shared_ptr<Core<Ts...>> core_;
};
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_FUTURE_H
#endif// TILE_BASE_FUTURE_FUTURE_H

View File

@ -27,287 +27,284 @@ 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;
NoCopy(const NoCopy &) = delete;
NoCopy &operator=(const NoCopy &) = delete;
NoCopy(NoCopy &&) = default;
NoCopy &operator=(NoCopy &&) = default;
NoCopy() = default;
NoCopy(const NoCopy &) = delete;
NoCopy &operator=(const NoCopy &) = delete;
NoCopy(NoCopy &&) = default;
NoCopy &operator=(NoCopy &&) = default;
};
Future<resource_ptr<void>, int> AcquireXxxAsync() {
Promise<resource_ptr<void>, int> p;
auto rc = p.GetFuture();
Future<resource_ptr<void>, int>
AcquireXxxAsync()
{
Promise<resource_ptr<void>, int> p;
auto rc = p.GetFuture();
NoCopy nc;
auto moved_nc = MakeMoveOnCopy(std::move(nc));
auto moved_promise = MakeMoveOnCopy(p);
std::thread([moved_promise]() mutable {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
auto v1 = resource_ptr<void>(reinterpret_cast<void *>(1), [](void *) {});
// moved_promise->SetValue(
// resource_ptr<void>(reinterpret_cast<void *>(1), [](void *) {}), 0);
moved_promise->SetValue(std::move(v1), 0);
}).detach();
NoCopy nc;
auto moved_nc = MakeMoveOnCopy(std::move(nc));
auto moved_promise = MakeMoveOnCopy(p);
std::thread([moved_promise]() mutable {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
auto v1 = resource_ptr<void>(reinterpret_cast<void *>(1), [](void *) {});
// moved_promise->SetValue(
// resource_ptr<void>(reinterpret_cast<void *>(1), [](void *) {}), 0);
moved_promise->SetValue(std::move(v1), 0);
}).detach();
return rc;
return rc;
}
TEST(Future, Initialization) {
// Uninitialized future
Future<> uf1;
Future<int, double> uf2;
TEST(Future, Initialization)
{
// Uninitialized future
Future<> uf1;
Future<int, double> uf2;
// Ready future
Future<> vf1(futurize_values);
Future<> tf1(futurize_tuple, std::make_tuple());
Future<int> fi(10);
Future<int, double> fid(futurize_values, 1, 2.0);
Future<int, float> f2(std::move(fid));
// Ready future
Future<> vf1(futurize_values);
Future<> tf1(futurize_tuple, std::make_tuple());
Future<int> fi(10);
Future<int, double> fid(futurize_values, 1, 2.0);
Future<int, float> f2(std::move(fid));
auto vf = MakeReadyFuture();
auto mf = MakeReadyFuture(1, 2.0);
auto vf = MakeReadyFuture();
auto mf = MakeReadyFuture(1, 2.0);
ASSERT_EQ(10, BlockingGet(std::move(fi)));
ASSERT_EQ(2.0, std::get<1>(BlockingGet(std::move(f2))));
ASSERT_EQ(2.0, std::get<1>(BlockingGet(std::move(mf))));
ASSERT_EQ(10, BlockingGet(std::move(fi)));
ASSERT_EQ(2.0, std::get<1>(BlockingGet(std::move(f2))));
ASSERT_EQ(2.0, std::get<1>(BlockingGet(std::move(mf))));
}
TEST(Future, WhenAll) {
static FILE *const kDummyFile = reinterpret_cast<FILE *>(0x10000123);
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};
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
};
Future<> void_op(futurize_values);
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) {
ASSERT_EQ(kDummyFile, std::get<0>(af));
ASSERT_EQ(0, std::get<1>(af));
ASSERT_EQ(nullptr, std::get<0>(ff));
ASSERT_EQ(-1, std::get<1>(ff));
ASSERT_EQ(nullptr, std::get<0>(mof));
ASSERT_EQ(-2, std::get<1>(mof));
called = true;
});
ASSERT_TRUE(called);
}
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) {
ASSERT_NE(nullptr, std::get<0>(a));
ASSERT_EQ(0, std::get<1>(a));
ASSERT_NE(nullptr, std::get<0>(b));
ASSERT_EQ(0, std::get<1>(b));
called = true;
}));
ASSERT_TRUE(called);
}
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) {
ASSERT_NE(nullptr, std::get<0>(p));
ASSERT_EQ(nullptr, std::get<1>(p));
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));
ASSERT_EQ(-1, std::get<1>(ff));
ASSERT_EQ(nullptr, std::get<0>(mof));
ASSERT_EQ(-2, std::get<1>(mof));
called = true;
called = true;
});
ASSERT_TRUE(called);
}
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) {
ASSERT_NE(nullptr, std::get<0>(a));
ASSERT_EQ(0, std::get<1>(a));
ASSERT_NE(nullptr, std::get<0>(b));
ASSERT_EQ(0, std::get<1>(b));
called = true;
}));
ASSERT_TRUE(called);
}
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) {
ASSERT_NE(nullptr, std::get<0>(p));
ASSERT_EQ(nullptr, std::get<1>(p));
called = true;
});
auto t1 = std::thread([&] {
latch.ArriveAndWait();
// set <ptr, null>
p1.SetValue(make_unique<int>(), std::unique_ptr<char>());
});
auto t2 = std::thread([&] {
latch.ArriveAndWait();
p2.SetValue();
});
ASSERT_FALSE(called);
latch.CountDown();
BlockingGet(std::move(all));
ASSERT_TRUE(called);
t1.join();
t2.join();
}
}
TEST(Future, WhenAllcollectionMultiThreaded)
{
for (int i = 0; i != 100; ++i) {
constexpr auto kCount = 100;
std::vector<Promise<bool>> vps(kCount);
std::vector<Future<bool>> vfs;
std::vector<std::thread> ts(kCount);
Latch latch(kCount + 1);
std::atomic<bool> called{false};
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; }));
called = true;
});
for (int i = 0; i != kCount; ++i) {
ts[i] = std::thread([&latch, &vps, i] {
latch.ArriveAndWait();
vps[i].SetValue(true);
});
auto t1 = std::thread([&] {
latch.ArriveAndWait();
// set <ptr, null>
p1.SetValue(make_unique<int>(), std::unique_ptr<char>());
});
auto t2 = std::thread([&] {
latch.ArriveAndWait();
p2.SetValue();
});
ASSERT_FALSE(called);
latch.CountDown();
BlockingGet(std::move(all));
ASSERT_TRUE(called);
t1.join();
t2.join();
}
}
ASSERT_FALSE(called);
latch.CountDown();
for (auto &&e : ts) { e.join(); }
ASSERT_TRUE(called);
}
}
TEST(Future, WhenAllcollectionMultiThreaded) {
for (int i = 0; i != 100; ++i) {
constexpr auto kCount = 100;
std::vector<Promise<bool>> vps(kCount);
std::vector<Future<bool>> vfs;
std::vector<std::thread> ts(kCount);
Latch latch(kCount + 1);
std::atomic<bool> called{false};
for (auto &p : vps) {
vfs.emplace_back(p.GetFuture());
TEST(Future, WhenAllCollectionEmpty)
{
{
std::vector<Future<>> vfs;
int x{};
WhenAll(std::move(vfs)).Then([&] { x = 10; });
ASSERT_EQ(10, x);
}
WhenAll(&vfs).Then([&](std::vector<bool> v) {
ASSERT_TRUE(std::all_of(v.begin(), v.end(), [&](bool v) { return v; }));
called = true;
});
for (int i = 0; i != kCount; ++i) {
ts[i] = std::thread([&latch, &vps, i] {
latch.ArriveAndWait();
vps[i].SetValue(true);
});
{
std::vector<Future<int>> vfs;
int x{};
WhenAll(std::move(vfs)).Then([&](std::vector<int> &&) { x = 10; });
ASSERT_EQ(10, x);
}
ASSERT_FALSE(called);
latch.CountDown();
for (auto &&e : ts) {
e.join();
}
ASSERT_TRUE(called);
}
}
TEST(Future, WhenAllCollectionEmpty) {
{
TEST(Future, WhenAllForCollectionOfEmptyFuture)
{
std::vector<Future<>> vfs;
int x{};
WhenAll(std::move(vfs)).Then([&] { x = 10; });
ASSERT_EQ(10, x);
}
bool cont_called = false;
{
std::vector<Future<int>> vfs;
int x{};
WhenAll(std::move(vfs)).Then([&](std::vector<int> &&) { x = 10; });
ASSERT_EQ(10, x);
}
for (int i = 0; i != 1000; ++i) { vfs.emplace_back(futurize_values); }
// std::vector<Future<>> is special, the continuation is called with
// no argument.
WhenAll(std::move(vfs)).Then([&] { cont_called = true; });
ASSERT_TRUE(cont_called);
}
TEST(Future, WhenAllForCollectionOfEmptyFuture) {
std::vector<Future<>> vfs;
bool cont_called = false;
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);
}
// std::vector<Future<>> is special, the continuation is called with
// no argument.
WhenAll(std::move(vfs)).Then([&] { cont_called = true; });
ASSERT_TRUE(cont_called);
WhenAny(std::move(vfs)).Then([&](std::size_t index) { called = true; });
ASSERT_TRUE(called);
}
TEST(Future, WhenAnyCollectionOfEmptyFuture) {
std::vector<Future<>> vfs;
bool called = false;
for (int i = 0; i != 1000; ++i) {
vfs.emplace_back(futurize_values);
}
TEST(Future, WhenAnyCollectionMultiThreaded)
{
for (int i = 0; i != 100; ++i) {
constexpr auto kCount = 100;
std::vector<Promise<std::unique_ptr<int>, char>> vps(kCount);
std::vector<Future<int>> vfs;
std::vector<std::thread> ts;
Latch latch(kCount + 1);
std::atomic<int> x{};
WhenAny(std::move(vfs)).Then([&](std::size_t index) { called = true; });
ASSERT_TRUE(called);
}
for (auto &&e : vps) {
vfs.emplace_back(e.GetFuture().Then([&](std::unique_ptr<int> &&setter_index, char &&) {
++x;
return *setter_index;
}));
}
TEST(Future, WhenAnyCollectionMultiThreaded) {
for (int i = 0; i != 100; ++i) {
constexpr auto kCount = 100;
std::vector<Promise<std::unique_ptr<int>, char>> vps(kCount);
std::vector<Future<int>> vfs;
std::vector<std::thread> ts;
Latch latch(kCount + 1);
std::atomic<int> x{};
auto all = WhenAny(std::move(vfs));
ASSERT_EQ(0, x);
for (auto &&e : vps) {
vfs.emplace_back(
e.GetFuture().Then([&](std::unique_ptr<int> &&setter_index, char &&) {
++x;
return *setter_index;
}));
for (int i = 0; i != vps.size(); ++i) {
auto ep = &vps[i];
ts.push_back(std::thread([&, ep, i] {
latch.ArriveAndWait();
ep->SetValue(make_unique<int>(i), 'a');
}));
}
ASSERT_EQ(0, x);
std::thread starter([&] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
latch.CountDown();
});
auto &&res = BlockingGet(&all);
auto &&index = std::get<0>(res);
auto &&value = std::get<1>(res);
ASSERT_GE(kCount, index);
ASSERT_LE(0, index);
ASSERT_EQ(index, value);
for (auto &&e : ts) { e.join(); }
starter.join();
ASSERT_EQ(kCount, x);
}
auto all = WhenAny(std::move(vfs));
ASSERT_EQ(0, x);
for (int i = 0; i != vps.size(); ++i) {
auto ep = &vps[i];
ts.push_back(std::thread([&, ep, i] {
latch.ArriveAndWait();
ep->SetValue(make_unique<int>(i), 'a');
}));
}
ASSERT_EQ(0, x);
std::thread starter([&] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
latch.CountDown();
});
auto &&res = BlockingGet(&all);
auto &&index = std::get<0>(res);
auto &&value = std::get<1>(res);
ASSERT_GE(kCount, index);
ASSERT_LE(0, index);
ASSERT_EQ(index, value);
for (auto &&e : ts) {
e.join();
}
starter.join();
ASSERT_EQ(kCount, x);
}
}
TEST(Future, DurationTimeout) {
auto one_sec = std::chrono::seconds(1);
{
Promise<int> p;
auto rc = BlockingTryGet(p.GetFuture(), one_sec);
ASSERT_FALSE(rc);
p.SetValue(10);
}
{
Promise<int> p;
auto f = p.GetFuture();
auto rc = BlockingTryGet(&f, one_sec);
ASSERT_FALSE(rc);
}
{
Promise<> p;
auto f = p.GetFuture();
auto rc = BlockingTryGet(&f, one_sec);
ASSERT_FALSE(rc);
}
TEST(Future, DurationTimeout)
{
auto one_sec = std::chrono::seconds(1);
{
Promise<int> p;
auto rc = BlockingTryGet(p.GetFuture(), one_sec);
ASSERT_FALSE(rc);
p.SetValue(10);
}
{
Promise<int> p;
auto f = p.GetFuture();
auto rc = BlockingTryGet(&f, one_sec);
ASSERT_FALSE(rc);
}
{
Promise<> p;
auto f = p.GetFuture();
auto rc = BlockingTryGet(&f, one_sec);
ASSERT_FALSE(rc);
}
}
} // namespace tile
}// namespace tile

View File

@ -11,32 +11,41 @@
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) {
f(std::forward<T>(t), std::integral_constant<std::size_t, I>());
template<std::size_t I, typename F, typename 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) {
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)
{
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)...);
for_each_indexed_impl<I + 1>(std::forward<F>(f), std::forward<Ts>(ts)...);
}
template <template <typename...> class TT, typename T>
template<template<typename...> class TT, typename T>
struct is_template_t : std::false_type {};
template <template <typename...> class TT, typename... T>
template<template<typename...> class TT, typename... T>
struct is_template_t<TT, TT<T...>> : std::true_type {};
template <typename T> struct is_duration {
static constexpr bool value = is_template_t<std::chrono::duration, T>::value;
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,23 +53,29 @@ 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 {
using type = std::optional<T>;
};
template <> struct optional_or_bool<void> {
using type = bool;
template<typename T>
struct optional_or_bool {
using type = std::optional<T>;
};
template <typename T>
template<>
struct optional_or_bool<void> {
using type = bool;
};
template<typename T>
using optional_or_bool_t = typename optional_or_bool<T>::type;
} // namespace detail
}// namespace detail
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)...);
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)...);
}
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_IMPLS_H
#endif// TILE_BASE_FUTURE_IMPLS_H

View File

@ -13,29 +13,37 @@
namespace tile {
namespace future {
template <class... Ts>
Promise<Ts...>::Promise()
: core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor())) {}
template<class... Ts>
Promise<Ts...>::Promise() : core_(std::make_shared<Core<Ts...>>(GetDefaultExecutor()))
{}
template <class... Ts> Future<Ts...> Promise<Ts...>::GetFuture() {
return Future<Ts...>(core_);
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) {
core_->SetBoxed(Boxed<Ts...>(box_values, std::forward<Us>(values)...));
template<class... Ts>
template<class... Us, class>
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) {
core_->SetBoxed(std::move(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))) {}
template<class... Ts>
Promise<Ts...>::Promise(Executor executor) : core_(std::make_shared<Core<Ts...>>(std::move(executor)))
{}
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_PROMISE_IMPL_H
#endif// TILE_BASE_FUTURE_PROMISE_IMPL_H

View File

@ -9,39 +9,39 @@
namespace tile {
namespace future {
template <typename... Ts> class Promise {
template<typename... Ts>
class Promise {
public:
using value_type = std::tuple<Ts...>;
using value_type = std::tuple<Ts...>;
Promise();
// Non-copyable
Promise(const Promise &) = delete;
Promise &operator=(const Promise &) = delete;
Promise();
// Non-copyable
Promise(const Promise &) = delete;
Promise &operator=(const Promise &) = delete;
// Movable.
Promise(Promise &&) = default;
Promise &operator=(Promise &&) = default;
// Movable.
Promise(Promise &&) = default;
Promise &operator=(Promise &&) = default;
static_assert(!types_contains<Types<Ts...>, void>::value,
"There's no point in specifying `void` in `Ts...`, use "
"`Promise<>` if you meant to declare a future with no value.");
static_assert(!types_contains<Types<Ts...>, void>::value,
"There's no point in specifying `void` in `Ts...`, use "
"`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>>
void SetValue(Us &&...values);
Future<Ts...> GetFuture();
template<typename... Us, typename = enable_if_t<std::is_constructible<value_type, Us &&...>::value>>
void SetValue(Us &&...values);
void SetBoxed(Boxed<Ts...> boxed);
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_;
explicit Promise(Executor executor);
std::shared_ptr<Core<Ts...>> core_;
};
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_PROMISE_H
#endif// TILE_BASE_FUTURE_PROMISE_H

View File

@ -13,52 +13,52 @@ namespace tile {
namespace future {
namespace detail {
template <typename... Ts>
auto SplitImpl(Future<Ts...> *future)
-> enable_if_t<(sizeof...(Ts) > 0), Future<Ts...>> {
Promise<Ts...> promise;
auto rc = promise.GetFuture();
template<typename... 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 {
auto moved_promise = MakeMoveOnCopy(promise);
*future = std::move(*future).Then([moved_promise](Boxed<Ts...> boxed) mutable {
moved_promise->SetBoxed(boxed);
return std::move(boxed.Get());
});
return rc;
});
return rc;
}
template <typename... Ts>
auto SplitImpl(Future<Ts...> *future)
-> enable_if_t<(sizeof...(Ts) == 0), Future<Ts...>> {
Promise<Ts...> promise;
auto rc = promise.GetFuture();
template<typename... 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);
});
auto moved_promise = MakeMoveOnCopy(promise);
*future = std::move(*future).Then([moved_promise](Boxed<Ts...> boxed) mutable { moved_promise->SetBoxed(boxed); });
return rc;
return rc;
}
} // namespace detail
}// 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) {
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)
{
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) {
return Split(&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);
}
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_SPLIT_H
#endif// TILE_BASE_FUTURE_SPLIT_H

View File

@ -8,69 +8,82 @@
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> {
using type = T;
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>
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>
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<> {
using type = Types<>;
template<>
struct types_cat<> {
using type = Types<>;
};
template <typename... Ts> struct types_cat<Types<Ts...>> {
using type = 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...> {};
template <typename... Ts> using types_cat_t = typename types_cat<Ts...>::type;
template<typename... Ts, typename... Us, typename... Vs>
struct types_cat<Types<Ts...>, Types<Us...>, Vs...> : types_cat<Types<Ts..., Us...>, Vs...> {};
template <typename T, typename U> struct types_contains;
template <typename U> struct types_contains<Types<>, U> : std::false_type {};
template <typename T, typename U>
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<Types<T>, U> : std::is_same<T, U> {};
template <typename T, typename U, typename... Ts>
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> {
using type = Types<>;
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>
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>
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
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_TYPES_H
#endif// TILE_BASE_FUTURE_TYPES_H

View File

@ -20,136 +20,151 @@
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> {
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_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> {
return cv.WaitUntil(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>
{
return cv.WaitUntil(lk, timeout);
}
template <typename... Ts, typename DurationOrTimePoint>
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>;
template <typename... Ts, typename DurationOrTimePoint>
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>
template<typename... Ts>
auto BlockingGetPreservingErrors(Future<Ts...> &&future) -> Boxed<Ts...>;
template <typename... Ts, typename DurationOrTimePoint>
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
}// 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>
template<typename... Ts>
auto BlockingGet(Future<Ts...> &&future) -> unboxed_type_t<Ts...>;
template <typename... Ts>
auto BlockingGet(Future<Ts...> *future) -> unboxed_type_t<Ts...> {
return BlockingGet(std::move(*future));
template<typename... 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...>> {
return future::detail::BlockingTryGet(std::move(future), timeout);
template<typename... Ts, typename DurationOrTimePoint>
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...>> {
return 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...>>
{
return BlockingTryGet(std::move(*future), timeout);
}
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
namespace tile {
namespace future {
namespace detail {
template <typename... Ts>
auto BlockingGetPreservingErrors(Future<Ts...> &&future) -> Boxed<Ts...> {
Mutex lock;
CondVar cv;
std::optional<Boxed<Ts...>> receiver;
std::move(future).Then([&](Boxed<Ts...> &&boxed) {
ScopedLock lk(lock);
receiver.emplace(std::move(boxed));
cv.NotifyOne();
});
ScopedLock lk(lock);
cv.Wait(lk, [&] { return receiver.has_value(); });
return std::move(*receiver);
}
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> {
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 rc = BlockingTryGetPreservingErrors(std::move(future), timeout);
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...>> {
struct State {
CondVar cv;
template<typename... Ts>
auto
BlockingGetPreservingErrors(Future<Ts...> &&future) -> Boxed<Ts...>
{
Mutex lock;
CondVar cv;
std::optional<Boxed<Ts...>> receiver;
};
auto state = std::make_shared<State>();
std::move(future).Then([&](Boxed<Ts...> &&boxed) {
ScopedLock lk(lock);
receiver.emplace(std::move(boxed));
cv.NotifyOne();
});
ScopedLock lk(lock);
cv.Wait(lk, [&] { return receiver.has_value(); });
return std::move(*receiver);
}
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>
{
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 rc = BlockingTryGetPreservingErrors(std::move(future), timeout);
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...>>
{
struct State {
CondVar cv;
Mutex lock;
std::optional<Boxed<Ts...>> receiver;
};
auto state = std::make_shared<State>();
std::move(future).Then([state](Boxed<Ts...> boxed) noexcept {
ScopedLock lk(state->lock);
state->receiver.emplace(std::move(boxed));
state->cv.NotifyOne();
});
std::move(future).Then([state](Boxed<Ts...> boxed) noexcept {
ScopedLock lk(state->lock);
state->receiver.emplace(std::move(boxed));
state->cv.NotifyOne();
});
ScopedLock lk(state->lock);
future::detail::WaitLockByCondVar(state->cv, lk, timeout);
return std::move(state->receiver);
future::detail::WaitLockByCondVar(state->cv, lk, timeout);
return std::move(state->receiver);
}
} // namespace detail
}// namespace detail
template <typename... Ts> Future<decay_t<Ts>...> MakeReadyFuture(Ts &&...args) {
return Future<decay_t<Ts>...>(futurize_values, std::forward<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...> {
return future::detail::BlockingGetPreservingErrors(std::move(future)).Get();
template<typename... Ts>
auto
BlockingGet(Future<Ts...> &&future) -> unboxed_type_t<Ts...>
{
return future::detail::BlockingGetPreservingErrors(std::move(future)).Get();
}
} // namespace future
} // namespace tile
#endif // TILE_BASE_FUTURE_UTILS_H
}// namespace future
}// namespace tile
#endif// TILE_BASE_FUTURE_UTILS_H

View File

@ -16,179 +16,171 @@ 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 {
return R(std::forward<T>(t));
};
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 {
return std::tuple_cat(Rebox(std::move(std::get<Is>(t).GetRaw()))...);
}
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()))...);
}
};
template <typename... Ts>
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::atomic<std::size_t> left{sizeof...(Ts)};
};
struct Context {
Promise<as_boxed_t<Ts>...> promise;
std::tuple<as_boxed_t<Ts>...> receiver{future::detail::RetrieveBoxed<as_boxed_t<Ts>>()...};
std::atomic<std::size_t> left{sizeof...(Ts)};
};
std::shared_ptr<Context> context{nullptr};
std::shared_ptr<Context> context{nullptr};
// template <int I, typename...> void ForEach() {}
// template <int I, typename...> void ForEach() {}
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::get<I>(context->receiver) = std::move(boxed);
if (context->left.fetch_sub(1) == 1) {
context->promise.SetValue(std::move(context->receiver));
}
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::get<I>(context->receiver) = std::move(boxed);
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) {
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>...> {
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 {
// Future<...>, Future<...>, ...
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...>());
return R(futurize_tuple, std::move(flattened));
});
}
};
template <template <typename...> class C, typename... Ts> struct WhenAllCImpl {
// Collection of futures
struct Context {
Promise<C<Boxed<Ts...>>> promise;
C<Boxed<Ts...>> values;
std::atomic<std::size_t> left;
};
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...>>());
}
auto context = std::make_shared<Context>();
context->values.reserve(futures.size());
context->left.store(futures.size());
template<int I, typename T, typename... Us>
void ForEach(T &&future, Us &&...futures)
{
ForEach<I>(std::move(future));
ForEach<I + 1>(std::move(futures)...);
}
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));
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>...>
{
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
{
// Future<...>, Future<...>, ...
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...>());
return R(futurize_tuple, std::move(flattened));
});
}
};
template<template<typename...> class C, typename... Ts>
struct WhenAllCImpl {
// Collection of futures
struct Context {
Promise<C<Boxed<Ts...>>> promise;
C<Boxed<Ts...>> values;
std::atomic<std::size_t> left;
};
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...>>()); }
auto context = std::make_shared<Context>();
context->values.reserve(futures.size());
context->left.store(futures.size());
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)); }
});
}
});
return context->promise.GetFuture();
}
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) {
C<unboxed_type_t<Ts...>> result;
result.reserve(boxed_values.size());
for (auto &&e : boxed_values) {
// Mac C++11 can't support empalce_back
result.push_back(std::move(e).Get());
}
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) {
// Mac C++11 can't support empalce_back
result.push_back(std::move(e).Get());
}
return result;
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) {
(void)boxed_values;
return;
}
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;
});
}
}
};
} // namespace detail
} // namespace future
} // namespace tile
}// namespace detail
}// namespace future
}// namespace tile
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)...)) {
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)...)) {
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,
tile::future::Future<>,
tile::future::Future<C<tile::future::unboxed_type_t<Ts...>>>>>
auto WhenAll(C<tile::future::Future<Ts...>> &&futures) -> R {
return future::detail::WhenAllCImpl<C, 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 <template <typename...> class C, class... Ts>
auto WhenAll(C<tile::future::Future<Ts...>> *futures)
-> decltype(WhenAll(std::move(*futures))) {
return 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)...);
}
} // namespace future
} // namespace tile
#endif // TILE_BASE_FUTURE_WHEN_ALL_H
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
{
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)))
{
return WhenAll(std::move(*futures));
}
}// namespace future
}// namespace tile
#endif// TILE_BASE_FUTURE_WHEN_ALL_H

View File

@ -10,62 +10,63 @@
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.");
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.");
struct Context {
Promise<std::size_t, Boxed<Ts...>> promise;
std::atomic<bool> ever_satisfied{false};
};
struct Context {
Promise<std::size_t, Boxed<Ts...>> promise;
std::atomic<bool> ever_satisfied{false};
};
auto context = std::make_shared<Context>();
auto context = std::make_shared<Context>();
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));
}
});
}
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)); }
});
}
return context->promise.GetFuture();
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>,
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) {
(void)values;
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) {
(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>,
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) {
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) {
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))) {
return WhenAny(std::move(*futures));
template<template<class...> class C, class... Ts>
auto
WhenAny(C<Future<Ts...>> *futures) -> decltype(WhenAny(std::move(*futures)))
{
return WhenAny(std::move(*futures));
}
} // namespace future
} // namespace tile
}// namespace future
}// namespace tile
#endif // TILE_BASE_FUTURE_WHEN_ANY_H
#endif// TILE_BASE_FUTURE_WHEN_ANY_H

View File

@ -10,84 +10,95 @@
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);
}
TILE_PCHECK(::close(fd) == 0);
}
};
namespace handle {
template <typename T, typename... Args>
constexpr T GetFirstArg(T first, Args...) {
return first;
template<typename T, typename... Args>
constexpr T
GetFirstArg(T first, Args...)
{
return first;
}
template <typename T> constexpr bool NotEqualToAnd(T val, T first) {
return val != 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) {
return val != first && NotEqualToAnd(val, std::forward<Args>(args)...);
template<typename T, typename... Args>
constexpr bool
NotEqualToAnd(T val, T first, Args... args)
{
return val != first && NotEqualToAnd(val, std::forward<Args>(args)...);
}
} // namespace handle
}// namespace handle
template <typename T, typename Deleter, T... kInvalidHandles>
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() = default;
GenericHandle(GenericHandle &&h) noexcept {
handle_ = h.handle_;
h.handle_ = kDefaultInvalid;
}
constexpr explicit GenericHandle(T handle) noexcept : handle_(handle) {}
GenericHandle &operator=(GenericHandle &&h) noexcept {
if (&h == this) {
return *this;
~GenericHandle() { Reset(); }
GenericHandle(GenericHandle &&h) noexcept
{
handle_ = h.handle_;
h.handle_ = kDefaultInvalid;
}
handle_ = h.handle_;
h.handle_ = kDefaultInvalid;
return *this;
}
GenericHandle &operator=(GenericHandle &&h) noexcept
{
if (&h == this) { return *this; }
GenericHandle(const GenericHandle &) = delete;
GenericHandle &operator=(const GenericHandle &) = delete;
T *Retrieve() noexcept { return &handle_; }
constexpr T Get() const noexcept { return handle_; }
constexpr explicit operator bool() const noexcept {
return ::tile::detail::handle::NotEqualToAnd(handle_, kInvalidHandles...);
}
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_);
handle_ = h.handle_;
h.handle_ = kDefaultInvalid;
return *this;
}
GenericHandle(const GenericHandle &) = delete;
GenericHandle &operator=(const GenericHandle &) = delete;
T *Retrieve() noexcept { return &handle_; }
constexpr T Get() const noexcept { return handle_; }
constexpr explicit operator bool() const noexcept
{
return ::tile::detail::handle::NotEqualToAnd(handle_, kInvalidHandles...);
}
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_); }
handle_ = new_value;
}
handle_ = new_value;
}
private:
T handle_ = kDefaultInvalid;
T handle_ = kDefaultInvalid;
};
} // namespace detail
using Handle = detail::GenericHandle<int, detail::HandleDeleter, -1, 0>;
} // namespace tile
}// namespace detail
#endif // TILE_BASE_HANDLE_H
using Handle = detail::GenericHandle<int, detail::HandleDeleter, -1, 0>;
}// namespace tile
#endif// TILE_BASE_HANDLE_H

View File

@ -8,24 +8,26 @@
namespace tile {
TEST(Handle, InvalidValues) {
Handle h(0), h2(-1), h3(1);
ASSERT_FALSE(h);
ASSERT_FALSE(h2);
ASSERT_TRUE(h3);
auto holder = h3.Leak();
(void)holder;
TEST(Handle, InvalidValues)
{
Handle h(0), h2(-1), h3(1);
ASSERT_FALSE(h);
ASSERT_FALSE(h2);
ASSERT_TRUE(h3);
auto holder = h3.Leak();
(void) holder;
}
TEST(Handle, ValidHandle) {
int fd = 0;
{
Handle h(open("/dev/null", O_WRONLY));
fd = h.Get();
ASSERT_EQ(1, write(fd, "1", 1));
ASSERT_TRUE(h);
}
ASSERT_EQ(-1, write(fd, "1", 1));
TEST(Handle, ValidHandle)
{
int fd = 0;
{
Handle h(open("/dev/null", O_WRONLY));
fd = h.Get();
ASSERT_EQ(1, write(fd, "1", 1));
ASSERT_TRUE(h);
}
ASSERT_EQ(-1, write(fd, "1", 1));
}
} // namespace tile
}// namespace tile

View File

@ -8,78 +8,82 @@
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 {
return AllocImpl<Traits>::Next();
template<typename Traits>
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;
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();
}
return SlowNext();
}
private:
static Type SlowNext();
private:
static Type SlowNext();
private:
static std::atomic<Type> global{Traits::kMin};
static thread_local Type current = 0;
static thread_local Type max = 0;
static std::atomic<Type> global{Traits::kMin};
static thread_local Type current = 0;
static thread_local Type max = 0;
};
template <typename Traits>
template<typename Traits>
std::atomic<typename Traits::Type> AllocImpl<Traits>::global;
template <typename Traits>
template<typename Traits>
thread_local typename Traits::Type AllocImpl<Traits>::current;
template <typename Traits>
template<typename Traits>
thread_local typename Traits::Type AllocImpl<Traits>::max;
template <typename Traits>
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, "");
static_assert(Traits::kBatchSize > 1, "");
template<typename Traits>
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, "");
static_assert(Traits::kBatchSize > 1, "");
Type read = global.load(std::memory_order_relaxed);
Type next;
do {
TILE_CHECK_GE(read, Traits::kMin);
TILE_CHECK_LE(read, Traits::kMax);
Type read = global.load(std::memory_order_relaxed);
Type next;
do {
TILE_CHECK_GE(read, Traits::kMin);
TILE_CHECK_LE(read, Traits::kMax);
current = read;
if (read > Traits::kMax - Traits::kBatchSize) {
// Overflowing
next = Traits::kMin;
next = Traits::kMax;
} else {
next = read + Traits::kBatchSize;
max = next;
}
} while (
!global.compare_exchange_weak(read, next, std::memory_order_relaxed));
current = read;
if (read > Traits::kMax - Traits::kBatchSize) {
// Overflowing
next = Traits::kMin;
next = Traits::kMax;
} else {
next = read + Traits::kBatchSize;
max = next;
}
} while (!global.compare_exchange_weak(read, next, std::memory_order_relaxed));
TILE_CHECK_GE(max, Traits::kMin);
TILE_CHECK_LE(max, Traits::kMax);
TILE_CHECK_LT(current, max);
TILE_CHECK_GE(current, Traits::kMin);
TILE_CHECK_LE(current, Traits::kMax);
TILE_CHECK_GE(max, Traits::kMin);
TILE_CHECK_LE(max, Traits::kMax);
TILE_CHECK_LT(current, max);
TILE_CHECK_GE(current, Traits::kMin);
TILE_CHECK_LE(current, Traits::kMax);
return Next();
return Next();
}
} // namespace id_alloc
} // namespace tile
}// namespace id_alloc
}// namespace tile
#endif // TILE_BASE_ID_ALLOC_H
#endif// TILE_BASE_ID_ALLOC_H

View File

@ -10,50 +10,55 @@
#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
namespace {}// namespace
BackgroundTaskHost *BackgroundTaskHost::Instance() {
static NeverDestroyedSingleton<BackgroundTaskHost> background_task_host;
return background_task_host.Get();
BackgroundTaskHost *
BackgroundTaskHost::Instance()
{
static NeverDestroyedSingleton<BackgroundTaskHost> background_task_host;
return background_task_host.Get();
}
void BackgroundTaskHost::Start() {
auto logical_cpus = std::thread::hardware_concurrency();
if (logical_cpus == 0) {
logical_cpus = 1;
TILE_LOG_WARNING("hardware_concurrency() return 0");
}
std::vector<int> affinity;
void
BackgroundTaskHost::Start()
{
auto logical_cpus = std::thread::hardware_concurrency();
if (logical_cpus == 0) {
logical_cpus = 1;
TILE_LOG_WARNING("hardware_concurrency() return 0");
}
std::vector<int> affinity;
pools_.resize(1);
pools_[0] = make_unique<ThreadPool>(
logical_cpus, affinity,
FLAGS_tile_background_task_host_workers_nice_value);
pools_.resize(1);
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::Stop()
{
for (auto &&e : pools_) { e->Stop(); }
}
void BackgroundTaskHost::Join() {
for (auto &&e : pools_) {
e->Join();
}
pools_.clear();
void
BackgroundTaskHost::Join()
{
for (auto &&e : pools_) { e->Join(); }
pools_.clear();
}
void BackgroundTaskHost::Queue(std::function<void()> &&op) {
TILE_CHECK_GT(pools_.size(), 0);
pools_[Random() % pools_.size()]->QueueJob(std::move(op));
void
BackgroundTaskHost::Queue(std::function<void()> &&op)
{
TILE_CHECK_GT(pools_.size(), 0);
pools_[Random() % pools_.size()]->QueueJob(std::move(op));
}
BackgroundTaskHost::BackgroundTaskHost() = default;
} // namespace internal
} // namespace tile
}// namespace internal
}// namespace tile

View File

@ -14,21 +14,21 @@ namespace tile {
namespace internal {
class BackgroundTaskHost {
public:
static BackgroundTaskHost *Instance();
void Start();
void Stop();
void Join();
static BackgroundTaskHost *Instance();
void Start();
void Stop();
void Join();
void Queue(std::function<void()> &&op);
void Queue(std::function<void()> &&op);
private:
friend class NeverDestroyedSingleton<BackgroundTaskHost>;
BackgroundTaskHost();
friend class NeverDestroyedSingleton<BackgroundTaskHost>;
BackgroundTaskHost();
private:
std::vector<std::unique_ptr<ThreadPool>> pools_;
std::vector<std::unique_ptr<ThreadPool>> pools_;
};
} // namespace internal
} // namespace tile
}// namespace internal
}// namespace tile
#endif // TILE_BASE_INTERNAL_BACKGROUND_TASK_HOST_H
#endif// TILE_BASE_INTERNAL_BACKGROUND_TASK_HOST_H

View File

@ -7,21 +7,22 @@
namespace tile {
namespace internal {
TEST(BackgroundTaskHost, All) {
// BackgroundTaskHost::Instance()->Start();
for (int i = 0; i < 1000; ++i) {
int x = 1;
Latch latch(1);
BackgroundTaskHost::Instance()->Queue([&] {
x = 2;
latch.CountDown();
});
latch.Wait();
ASSERT_EQ(2, x);
}
// BackgroundTaskHost::Instance()->Stop();
// BackgroundTaskHost::Instance()->Join();
TEST(BackgroundTaskHost, All)
{
// BackgroundTaskHost::Instance()->Start();
for (int i = 0; i < 1000; ++i) {
int x = 1;
Latch latch(1);
BackgroundTaskHost::Instance()->Queue([&] {
x = 2;
latch.CountDown();
});
latch.Wait();
ASSERT_EQ(2, x);
}
// BackgroundTaskHost::Instance()->Stop();
// BackgroundTaskHost::Instance()->Join();
}
} // namespace internal
} // namespace tile
}// namespace internal
}// namespace tile

View File

@ -12,31 +12,27 @@ namespace tile {
namespace internal {
namespace detail {
struct CaseInsensitiveHash {
std::size_t operator()(Slice s) const noexcept {
uint64_t hash = 5381;
for (auto &&c : s) {
hash += ((hash << 5) + hash) + ToLower(c);
std::size_t operator()(Slice s) const noexcept
{
uint64_t hash = 5381;
for (auto &&c : s) { hash += ((hash << 5) + hash) + ToLower(c); }
return hash;
}
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);
}
return EqualsIgnoreCase(x, y);
}
};
} // namespace detail
}// namespace detail
template <typename K, typename V>
using CaseInsensitiveHashMap =
std::unordered_map<K, V, detail::CaseInsensitiveHash,
detail::CaseInsensitiveEqualTo>;
} // namespace internal
} // namespace tile
template<typename K, typename V>
using CaseInsensitiveHashMap = std::unordered_map<K, V, detail::CaseInsensitiveHash, detail::CaseInsensitiveEqualTo>;
}// namespace internal
}// namespace tile
#endif // _TILE_BASE_INTERNAL_CASE_INSENSITIVE_HASH_MAP_H
#endif// _TILE_BASE_INTERNAL_CASE_INSENSITIVE_HASH_MAP_H

View File

@ -6,20 +6,21 @@
namespace tile {
namespace internal {
TEST(CaseInsensitiveHashMap, Easy) {
CaseInsensitiveHashMap<std::string, std::string> m;
TEST(CaseInsensitiveHashMap, Easy)
{
CaseInsensitiveHashMap<std::string, std::string> m;
m["a"] = "1";
m["b"] = "10";
m["C"] = "-5";
m["c"] = "3"; // Overwrites "C"
m["a"] = "1";
m["b"] = "10";
m["C"] = "-5";
m["c"] = "3";// Overwrites "C"
ASSERT_EQ("1", m["a"]);
ASSERT_EQ("1", m["A"]);
ASSERT_EQ("3", m["C"]);
ASSERT_FALSE(m.count("d"));
ASSERT_EQ(3, m.size());
ASSERT_EQ("1", m["a"]);
ASSERT_EQ("1", m["A"]);
ASSERT_EQ("3", m["C"]);
ASSERT_FALSE(m.count("d"));
ASSERT_EQ(3, m.size());
}
} // namespace internal
} // namespace tile
}// namespace internal
}// namespace tile

View File

@ -22,132 +22,126 @@ 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) {
auto bytes = size * nmemb;
// What ?
static_cast<std::string *>(pstr)->append(ptr, bytes);
return bytes;
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) {
(*share_handle_mutex)[data].Lock();
}
void ReleaseLockOfShareHandle(CURL *handle, curl_lock_data data,
void *userptr) {
(*share_handle_mutex)[data].Unlock();
void
AcquireLockOfShareHandle(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr)
{
(*share_handle_mutex)[data].Lock();
}
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);
return share;
}());
return share->get();
void
ReleaseLockOfShareHandle(CURL *handle, curl_lock_data data, void *userptr)
{
(*share_handle_mutex)[data].Unlock();
}
#define CURL_SETOPT(handle, opt, val) \
TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(handle, opt, val))
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);
return share;
}());
return share->get();
}
#define CURL_SETOPT(handle, opt, val) TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(handle, opt, val))
}// namespace
} // 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());
}
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()); }
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());
CURL_SETOPT(curl.get(), CURLOPT_POSTFIELDSIZE_LARGE, body.size());
CURL_SETOPT(curl.get(), CURLOPT_HTTPHEADER, hdrs);
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_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_URL, uri.c_str());
CURL_SETOPT(curl.get(), CURLOPT_POSTFIELDS, body.c_str());
CURL_SETOPT(curl.get(), CURLOPT_POSTFIELDSIZE_LARGE, body.size());
CURL_SETOPT(curl.get(), CURLOPT_HTTPHEADER, hdrs);
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_SHARE, GetCurlShareHandle());
// 10 min
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));
return std::make_unexpected(-rc);
}
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));
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);
}
return result;
long resp_code;
curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &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};
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));
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));
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);
}
return result;
std::string result;
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));
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));
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); }
return result;
}
void SetHttpPostMockHandler(HttpPostMockHandler &&http_post_mock_handler) {
s_http_post_mock_handler = std::move(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) {
s_http_get_mock_handler = std::move(http_get_mock_handler);
void
SetHttpGetMockHandler(HttpGetMockHandler &&http_get_mock_handler)
{
s_http_get_mock_handler = std::move(http_get_mock_handler);
}
} // namespace internal
} // namespace tile
}// namespace internal
}// namespace tile

Some files were not shown because too many files have changed in this diff Show More