mirror of
https://github.com/vale981/tdesktop
synced 2025-03-05 09:41:41 -05:00
Use lib_rpl / lib_base from submodules.
This commit is contained in:
parent
be9398b05a
commit
1b89348d89
118 changed files with 128 additions and 16366 deletions
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QLatin1String>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Type>
|
||||
inline Type take(Type &value) {
|
||||
return std::exchange(value, Type {});
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type duplicate(const Type &value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
inline constexpr size_t array_size(const Type(&)[Size]) {
|
||||
return Size;
|
||||
}
|
||||
|
||||
template <typename Container, typename T>
|
||||
inline bool contains(const Container &container, const T &value) {
|
||||
const auto end = std::end(container);
|
||||
return std::find(std::begin(container), end, value) != end;
|
||||
}
|
||||
|
||||
template <typename D, typename T>
|
||||
inline constexpr D up_cast(T object) {
|
||||
using DV = std::decay_t<decltype(*D())>;
|
||||
using TV = std::decay_t<decltype(*T())>;
|
||||
if constexpr (std::is_base_of_v<DV, TV>) {
|
||||
return object;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// We need a custom comparator for set<std::unique_ptr<T>>::find to work with pointers.
|
||||
// thanks to http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs
|
||||
template <typename T>
|
||||
struct pointer_comparator {
|
||||
using is_transparent = std::true_type;
|
||||
|
||||
// helper does some magic in order to reduce the number of
|
||||
// pairs of types we need to know how to compare: it turns
|
||||
// everything into a pointer, and then uses `std::less<T*>`
|
||||
// to do the comparison:
|
||||
struct helper {
|
||||
const T *ptr = nullptr;
|
||||
helper() = default;
|
||||
helper(const helper &other) = default;
|
||||
helper(const T *p) : ptr(p) {
|
||||
}
|
||||
template <typename ...Ts>
|
||||
helper(const std::shared_ptr<Ts...> &other) : ptr(other.get()) {
|
||||
}
|
||||
template <typename ...Ts>
|
||||
helper(const std::unique_ptr<Ts...> &other) : ptr(other.get()) {
|
||||
}
|
||||
bool operator<(helper other) const {
|
||||
return std::less<const T*>()(ptr, other.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// without helper, we'd need 2^n different overloads, where
|
||||
// n is the number of types we want to support (so, 8 with
|
||||
// raw pointers, unique pointers, and shared pointers). That
|
||||
// seems silly.
|
||||
// && helps enforce rvalue use only
|
||||
bool operator()(const helper &&lhs, const helper &&rhs) const {
|
||||
return lhs < rhs;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline QString FromUtf8Safe(const char *string, int size = -1) {
|
||||
if (!string || !size) {
|
||||
return QString();
|
||||
} else if (size < 0) {
|
||||
size = strlen(string);
|
||||
}
|
||||
const auto result = QString::fromUtf8(string, size);
|
||||
const auto back = result.toUtf8();
|
||||
return (back.size() != size || memcmp(back.constData(), string, size))
|
||||
? QString::fromLocal8Bit(string, size)
|
||||
: result;
|
||||
}
|
||||
|
||||
inline QString FromUtf8Safe(const QByteArray &string) {
|
||||
return FromUtf8Safe(string.constData(), string.size());
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
template <typename T>
|
||||
inline void accumulate_max(T &a, const T &b) { if (a < b) a = b; }
|
||||
|
||||
template <typename T>
|
||||
inline void accumulate_min(T &a, const T &b) { if (a > b) a = b; }
|
||||
|
||||
template <size_t Size>
|
||||
QLatin1String qstr(const char(&string)[Size]) {
|
||||
return QLatin1String(string, Size - 1);
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "base/index_based_iterator.h"
|
||||
|
||||
TEST_CASE("index_based_iterator tests", "[base::algorithm]") {
|
||||
auto v = std::vector<int>();
|
||||
|
||||
v.insert(v.end(), { 1, 2, 3, 4, 5, 4, 3, 2, 1 });
|
||||
auto push_back_safe_remove_if = [](auto &v, auto predicate) {
|
||||
auto begin = base::index_based_begin(v);
|
||||
auto end = base::index_based_end(v);
|
||||
auto from = std::remove_if(begin, end, predicate);
|
||||
if (from != end) {
|
||||
auto newEnd = base::index_based_end(v);
|
||||
if (newEnd != end) {
|
||||
REQUIRE(newEnd > end);
|
||||
while (end != newEnd) {
|
||||
*from++ = *end++;
|
||||
}
|
||||
}
|
||||
v.erase(from.base(), newEnd.base());
|
||||
}
|
||||
};
|
||||
SECTION("allows to push_back from predicate") {
|
||||
push_back_safe_remove_if(v, [&v](int value) {
|
||||
v.push_back(value);
|
||||
return (value % 2) == 1;
|
||||
});
|
||||
auto expected = std::vector<int> { 2, 4, 4, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1 };
|
||||
REQUIRE(v == expected);
|
||||
}
|
||||
|
||||
SECTION("allows to push_back while removing all") {
|
||||
push_back_safe_remove_if(v, [&v](int value) {
|
||||
if (value == 5) {
|
||||
v.push_back(value);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
auto expected = std::vector<int> { 5 };
|
||||
REQUIRE(v == expected);
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
// Ensures/Expects.
|
||||
#include <gsl/gsl_assert>
|
||||
|
||||
namespace base {
|
||||
namespace assertion {
|
||||
|
||||
// Client must define that method.
|
||||
void log(const char *message, const char *file, int line);
|
||||
|
||||
// Release build assertions.
|
||||
inline constexpr void noop() {
|
||||
}
|
||||
|
||||
[[noreturn]] inline void fail(
|
||||
const char *message,
|
||||
const char *file,
|
||||
int line) {
|
||||
log(message, file, line);
|
||||
|
||||
// Crash with access violation and generate crash report.
|
||||
volatile auto nullptr_value = (int*)nullptr;
|
||||
*nullptr_value = 0;
|
||||
|
||||
// Silent the possible failure to comply noreturn warning.
|
||||
std::abort();
|
||||
}
|
||||
|
||||
constexpr const char* extract_basename(const char* path, size_t size) {
|
||||
while (size != 0 && path[size - 1] != '/' && path[size - 1] != '\\') {
|
||||
--size;
|
||||
}
|
||||
return path + size;
|
||||
}
|
||||
|
||||
} // namespace assertion
|
||||
} // namespace base
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define AssertUnlikelyHelper(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define AssertUnlikelyHelper(x) (!!(x))
|
||||
#endif
|
||||
|
||||
#define AssertValidationCondition(condition, message, file, line)\
|
||||
((AssertUnlikelyHelper(!(condition)))\
|
||||
? ::base::assertion::fail(message, file, line)\
|
||||
: ::base::assertion::noop())
|
||||
|
||||
#define SOURCE_FILE_BASENAME (::base::assertion::extract_basename(\
|
||||
__FILE__,\
|
||||
sizeof(__FILE__)))
|
||||
|
||||
#define AssertCustom(condition, message) (AssertValidationCondition(\
|
||||
condition,\
|
||||
message,\
|
||||
SOURCE_FILE_BASENAME,\
|
||||
__LINE__))
|
||||
#define Assert(condition) AssertCustom(condition, "\"" #condition "\"")
|
||||
|
||||
// Define our own versions of Expects() and Ensures().
|
||||
// Let them crash with reports and logging.
|
||||
#ifdef Expects
|
||||
#undef Expects
|
||||
#endif // Expects
|
||||
#define Expects(condition) (AssertValidationCondition(\
|
||||
condition,\
|
||||
"\"" #condition "\"",\
|
||||
SOURCE_FILE_BASENAME,\
|
||||
__LINE__))
|
||||
|
||||
#ifdef Ensures
|
||||
#undef Ensures
|
||||
#endif // Ensures
|
||||
#define Ensures(condition) (AssertValidationCondition(\
|
||||
condition,\
|
||||
"\"" #condition "\"",\
|
||||
SOURCE_FILE_BASENAME,\
|
||||
__LINE__))
|
||||
|
||||
#ifdef Unexpected
|
||||
#undef Unexpected
|
||||
#endif // Unexpected
|
||||
#define Unexpected(message) (::base::assertion::fail(\
|
||||
"Unexpected: " message,\
|
||||
SOURCE_FILE_BASENAME,\
|
||||
__LINE__))
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define AssertIsDebug(...)
|
||||
#endif // _DEBUG
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
|
||||
// Methods that must be implemented outside lib_base.
|
||||
|
||||
namespace base {
|
||||
|
||||
void EnterFromEventLoop(FnMut<void()> &&method);
|
||||
|
||||
} // namespace
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/base_pch.h"
|
||||
|
||||
// Precompiled header helper.
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <crl/crl.h>
|
||||
#include <rpl/rpl.h>
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
#include <range/v3/all.hpp>
|
||||
|
||||
#include "base/flat_map.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/openssl_help.h"
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/build_config.h"
|
||||
#include "base/ordered_set.h"
|
||||
#include "base/unique_function.h"
|
||||
#include "base/functors.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QtCore/QByteArray>
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <gsl/gsl>
|
||||
|
||||
namespace func = base::functors;
|
||||
|
||||
using gsl::not_null;
|
||||
using index_type = gsl::index;
|
||||
using size_type = gsl::index;
|
||||
|
||||
template <typename Signature>
|
||||
using Fn = std::function<Signature>;
|
||||
|
||||
template <typename Signature>
|
||||
using FnMut = base::unique_function<Signature>;
|
||||
|
||||
//using uchar = unsigned char; // Qt has uchar
|
||||
using int8 = qint8;
|
||||
using uint8 = quint8;
|
||||
using int16 = qint16;
|
||||
using uint16 = quint16;
|
||||
using int32 = qint32;
|
||||
using uint32 = quint32;
|
||||
using int64 = qint64;
|
||||
using uint64 = quint64;
|
||||
using float32 = float;
|
||||
using float64 = double;
|
||||
|
||||
using TimeId = int32;
|
||||
|
||||
// Define specializations for QByteArray for Qt 5.3.2, because
|
||||
// QByteArray in Qt 5.3.2 doesn't declare "pointer" subtype.
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
|
||||
namespace gsl {
|
||||
|
||||
template <>
|
||||
inline span<char> make_span<QByteArray>(QByteArray &cont) {
|
||||
return span<char>(cont.data(), cont.size());
|
||||
}
|
||||
|
||||
template <>
|
||||
inline span<const char> make_span(const QByteArray &cont) {
|
||||
return span<const char>(cont.constData(), cont.size());
|
||||
}
|
||||
|
||||
} // namespace gsl
|
||||
#endif // OS_MAC_OLD
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/algorithm.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace base {
|
||||
|
||||
class binary_guard {
|
||||
public:
|
||||
binary_guard() = default;
|
||||
binary_guard(binary_guard &&other);
|
||||
binary_guard &operator=(binary_guard &&other);
|
||||
~binary_guard();
|
||||
|
||||
binary_guard &operator=(std::nullptr_t);
|
||||
|
||||
bool alive() const;
|
||||
binary_guard make_guard();
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
private:
|
||||
void destroy();
|
||||
|
||||
std::atomic<bool> *_bothAlive = nullptr;
|
||||
|
||||
};
|
||||
|
||||
inline binary_guard::binary_guard(binary_guard &&other)
|
||||
: _bothAlive(base::take(other._bothAlive)) {
|
||||
}
|
||||
|
||||
inline binary_guard &binary_guard::operator=(binary_guard &&other) {
|
||||
if (this != &other) {
|
||||
destroy();
|
||||
_bothAlive = base::take(other._bothAlive);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline binary_guard::~binary_guard() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
inline binary_guard &binary_guard::operator=(std::nullptr_t) {
|
||||
destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline binary_guard::operator bool() const {
|
||||
return alive();
|
||||
}
|
||||
|
||||
inline bool binary_guard::alive() const {
|
||||
return _bothAlive && _bothAlive->load();
|
||||
}
|
||||
|
||||
inline void binary_guard::destroy() {
|
||||
if (const auto both = base::take(_bothAlive)) {
|
||||
auto old = true;
|
||||
if (!both->compare_exchange_strong(old, false)) {
|
||||
delete both;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline binary_guard binary_guard::make_guard() {
|
||||
destroy();
|
||||
|
||||
auto result = binary_guard();
|
||||
_bothAlive = result._bothAlive = new std::atomic<bool>(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename T, typename Enable>
|
||||
struct guard_traits;
|
||||
|
||||
template <>
|
||||
struct guard_traits<base::binary_guard, void> {
|
||||
static base::binary_guard create(base::binary_guard value) {
|
||||
return value;
|
||||
}
|
||||
static bool check(const base::binary_guard &guard) {
|
||||
return guard.alive();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// thanks Chromium
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define OS_MAC 1
|
||||
#elif defined(__linux__) // __APPLE__
|
||||
#define OS_LINUX 1
|
||||
#elif defined(_WIN32) // __APPLE__ || __linux__
|
||||
#define OS_WIN 1
|
||||
#else // __APPLE__ || __linux__ || _WIN32
|
||||
#error Please add support for your platform in base/build_config.h
|
||||
#endif // else for __APPLE__ || __linux__ || _WIN32
|
||||
|
||||
// For access to standard POSIXish features, use OS_POSIX instead of a
|
||||
// more specific macro.
|
||||
#if defined(OS_MAC) || defined(OS_LINUX)
|
||||
#define OS_POSIX 1
|
||||
#endif // OS_MAC || OS_LINUX
|
||||
|
||||
// Compiler detection.
|
||||
#if defined(__clang__)
|
||||
#define COMPILER_CLANG 1
|
||||
#elif defined(__GNUC__) // __clang__
|
||||
#define COMPILER_GCC 1
|
||||
#elif defined(_MSC_VER) // __clang__ || __GNUC__
|
||||
#define COMPILER_MSVC 1
|
||||
#else // _MSC_VER || __clang__ || __GNUC__
|
||||
#error Please add support for your compiler in base/build_config.h
|
||||
#endif // else for _MSC_VER || __clang__ || __GNUC__
|
||||
|
||||
// Processor architecture detection.
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
#define ARCH_CPU_X86_FAMILY 1
|
||||
#define ARCH_CPU_X86_64 1
|
||||
#define ARCH_CPU_64_BITS 1
|
||||
#elif defined(_M_IX86) || defined(__i386__)
|
||||
#define ARCH_CPU_X86_FAMILY 1
|
||||
#define ARCH_CPU_X86 1
|
||||
#define ARCH_CPU_32_BITS 1
|
||||
#elif defined(__aarch64__)
|
||||
#define ARCH_CPU_64_BITS 1
|
||||
#elif defined(_M_ARM) || defined(__arm__)
|
||||
#define ARCH_CPU_32_BITS 1
|
||||
#else
|
||||
#error Please add support for your architecture in base/build_config.h
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define TG_FORCE_INLINE inline __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define TG_FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define TG_FORCE_INLINE inline
|
||||
#endif
|
||||
|
||||
#include <climits>
|
||||
static_assert(CHAR_BIT == 8, "Not supported char size.");
|
|
@ -1,164 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
#include <gsl/gsl>
|
||||
#include <gsl/gsl_byte>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace bytes {
|
||||
|
||||
using type = gsl::byte;
|
||||
using span = gsl::span<type>;
|
||||
using const_span = gsl::span<const type>;
|
||||
using vector = std::vector<type>;
|
||||
|
||||
template <gsl::index Size>
|
||||
using array = std::array<type, Size>;
|
||||
|
||||
inline span make_detached_span(QByteArray &container) {
|
||||
return gsl::as_writeable_bytes(gsl::make_span(container));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Container,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_const_v<Container>
|
||||
&& !std::is_same_v<Container, QByteArray>>>
|
||||
inline span make_span(Container &container) {
|
||||
return gsl::as_writeable_bytes(gsl::make_span(container));
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline const_span make_span(const Container &container) {
|
||||
return gsl::as_bytes(gsl::make_span(container));
|
||||
}
|
||||
|
||||
template <typename Type, std::ptrdiff_t Extent>
|
||||
inline span make_span(gsl::span<Type, Extent> container) {
|
||||
return gsl::as_writeable_bytes(container);
|
||||
}
|
||||
|
||||
template <typename Type, std::ptrdiff_t Extent>
|
||||
inline const_span make_span(gsl::span<const Type, Extent> container) {
|
||||
return gsl::as_bytes(container);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline span make_span(Type *value, std::size_t count) {
|
||||
return gsl::as_writeable_bytes(gsl::make_span(value, count));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline const_span make_span(const Type *value, std::size_t count) {
|
||||
return gsl::as_bytes(gsl::make_span(value, count));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline span object_as_span(Type *value) {
|
||||
return bytes::make_span(value, 1);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline const_span object_as_span(const Type *value) {
|
||||
return bytes::make_span(value, 1);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline vector make_vector(const Container &container) {
|
||||
const auto buffer = bytes::make_span(container);
|
||||
return { buffer.begin(), buffer.end() };
|
||||
}
|
||||
|
||||
inline void copy(span destination, const_span source) {
|
||||
Expects(destination.size() >= source.size());
|
||||
|
||||
memcpy(destination.data(), source.data(), source.size());
|
||||
}
|
||||
|
||||
inline void move(span destination, const_span source) {
|
||||
Expects(destination.size() >= source.size());
|
||||
|
||||
memmove(destination.data(), source.data(), source.size());
|
||||
}
|
||||
|
||||
inline void set_with_const(span destination, type value) {
|
||||
memset(
|
||||
destination.data(),
|
||||
gsl::to_integer<unsigned char>(value),
|
||||
destination.size());
|
||||
}
|
||||
|
||||
inline int compare(const_span a, const_span b) {
|
||||
const auto aSize = a.size(), bSize = b.size();
|
||||
return (aSize > bSize)
|
||||
? 1
|
||||
: (aSize < bSize)
|
||||
? -1
|
||||
: memcmp(a.data(), b.data(), aSize);
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Arg>
|
||||
std::size_t spansLength(Arg &&arg) {
|
||||
return bytes::make_span(arg).size();
|
||||
}
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
std::size_t spansLength(Arg &&arg, Args &&...args) {
|
||||
return bytes::make_span(arg).size() + spansLength(args...);
|
||||
}
|
||||
|
||||
template <typename Arg>
|
||||
void spansAppend(span destination, Arg &&arg) {
|
||||
bytes::copy(destination, bytes::make_span(arg));
|
||||
}
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
void spansAppend(span destination, Arg &&arg, Args &&...args) {
|
||||
const auto data = bytes::make_span(arg);
|
||||
bytes::copy(destination, data);
|
||||
spansAppend(destination.subspan(data.size()), args...);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
vector concatenate(Args &&...args) {
|
||||
const auto size = details::spansLength(args...);
|
||||
auto result = vector(size);
|
||||
details::spansAppend(make_span(result), args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <
|
||||
typename SpanRange>
|
||||
vector concatenate(SpanRange args) {
|
||||
auto size = std::size_t(0);
|
||||
for (const auto &arg : args) {
|
||||
size += bytes::make_span(arg).size();
|
||||
}
|
||||
auto result = vector(size);
|
||||
auto buffer = make_span(result);
|
||||
for (const auto &arg : args) {
|
||||
const auto part = bytes::make_span(arg);
|
||||
bytes::copy(buffer, part);
|
||||
buffer = buffer.subspan(part.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Implemented in base/openssl_help.h
|
||||
void set_random(span destination);
|
||||
|
||||
} // namespace bytes
|
|
@ -1,364 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/concurrent_timer.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
using namespace base::details;
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
namespace {
|
||||
|
||||
constexpr auto kCallDelayedEvent = QEvent::Type(QEvent::User + 1);
|
||||
constexpr auto kCancelTimerEvent = QEvent::Type(QEvent::User + 2);
|
||||
static_assert(kCancelTimerEvent < QEvent::MaxUser);
|
||||
|
||||
ConcurrentTimerEnvironment *Environment/* = nullptr*/;
|
||||
QMutex EnvironmentMutex;
|
||||
|
||||
class CallDelayedEvent : public QEvent {
|
||||
public:
|
||||
CallDelayedEvent(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method);
|
||||
|
||||
crl::time timeout() const;
|
||||
Qt::TimerType type() const;
|
||||
FnMut<void()> takeMethod();
|
||||
|
||||
private:
|
||||
crl::time _timeout = 0;
|
||||
Qt::TimerType _type = Qt::PreciseTimer;
|
||||
FnMut<void()> _method;
|
||||
|
||||
};
|
||||
|
||||
class CancelTimerEvent : public QEvent {
|
||||
public:
|
||||
CancelTimerEvent();
|
||||
|
||||
};
|
||||
|
||||
CallDelayedEvent::CallDelayedEvent(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method)
|
||||
: QEvent(kCallDelayedEvent)
|
||||
, _timeout(timeout)
|
||||
, _type(type)
|
||||
, _method(std::move(method)) {
|
||||
Expects(_timeout >= 0 && _timeout < std::numeric_limits<int>::max());
|
||||
}
|
||||
|
||||
crl::time CallDelayedEvent::timeout() const {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
Qt::TimerType CallDelayedEvent::type() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
FnMut<void()> CallDelayedEvent::takeMethod() {
|
||||
return base::take(_method);
|
||||
}
|
||||
|
||||
CancelTimerEvent::CancelTimerEvent() : QEvent(kCancelTimerEvent) {
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class TimerObject : public QObject {
|
||||
public:
|
||||
TimerObject(
|
||||
not_null<QThread*> thread,
|
||||
not_null<QObject*> adjuster,
|
||||
Fn<void()> adjust);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *e) override;
|
||||
|
||||
private:
|
||||
void callDelayed(not_null<CallDelayedEvent*> e);
|
||||
void callNow();
|
||||
void cancel();
|
||||
void adjust();
|
||||
|
||||
FnMut<void()> _next;
|
||||
Fn<void()> _adjust;
|
||||
int _timerId = 0;
|
||||
|
||||
};
|
||||
|
||||
TimerObject::TimerObject(
|
||||
not_null<QThread*> thread,
|
||||
not_null<QObject*> adjuster,
|
||||
Fn<void()> adjust)
|
||||
: _adjust(std::move(adjust)) {
|
||||
moveToThread(thread);
|
||||
connect(
|
||||
adjuster,
|
||||
&QObject::destroyed,
|
||||
this,
|
||||
&TimerObject::adjust,
|
||||
Qt::DirectConnection);
|
||||
}
|
||||
|
||||
bool TimerObject::event(QEvent *e) {
|
||||
const auto type = e->type();
|
||||
switch (type) {
|
||||
case kCallDelayedEvent:
|
||||
callDelayed(static_cast<CallDelayedEvent*>(e));
|
||||
return true;
|
||||
case kCancelTimerEvent:
|
||||
cancel();
|
||||
return true;
|
||||
case QEvent::Timer:
|
||||
callNow();
|
||||
return true;
|
||||
}
|
||||
return QObject::event(e);
|
||||
}
|
||||
|
||||
void TimerObject::callDelayed(not_null<CallDelayedEvent*> e) {
|
||||
cancel();
|
||||
|
||||
const auto timeout = e->timeout();
|
||||
const auto type = e->type();
|
||||
_next = e->takeMethod();
|
||||
if (timeout > 0) {
|
||||
_timerId = startTimer(timeout, type);
|
||||
} else {
|
||||
base::take(_next)();
|
||||
}
|
||||
}
|
||||
|
||||
void TimerObject::cancel() {
|
||||
if (const auto id = base::take(_timerId)) {
|
||||
killTimer(id);
|
||||
}
|
||||
_next = nullptr;
|
||||
}
|
||||
|
||||
void TimerObject::callNow() {
|
||||
auto next = base::take(_next);
|
||||
cancel();
|
||||
next();
|
||||
}
|
||||
|
||||
void TimerObject::adjust() {
|
||||
if (_adjust) {
|
||||
_adjust();
|
||||
}
|
||||
}
|
||||
|
||||
TimerObjectWrap::TimerObjectWrap(Fn<void()> adjust) {
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
|
||||
if (Environment) {
|
||||
_value = Environment->createTimer(std::move(adjust));
|
||||
}
|
||||
}
|
||||
|
||||
TimerObjectWrap::~TimerObjectWrap() {
|
||||
if (_value) {
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
|
||||
if (Environment) {
|
||||
_value.release()->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimerObjectWrap::call(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method) {
|
||||
sendEvent(std::make_unique<CallDelayedEvent>(
|
||||
timeout,
|
||||
type,
|
||||
std::move(method)));
|
||||
}
|
||||
|
||||
void TimerObjectWrap::cancel() {
|
||||
sendEvent(std::make_unique<CancelTimerEvent>());
|
||||
}
|
||||
|
||||
void TimerObjectWrap::sendEvent(std::unique_ptr<QEvent> event) {
|
||||
if (!_value) {
|
||||
return;
|
||||
}
|
||||
QCoreApplication::postEvent(
|
||||
_value.get(),
|
||||
event.release(),
|
||||
Qt::HighEventPriority);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
ConcurrentTimerEnvironment::ConcurrentTimerEnvironment() {
|
||||
_thread.start();
|
||||
_adjuster.moveToThread(&_thread);
|
||||
|
||||
acquire();
|
||||
}
|
||||
|
||||
ConcurrentTimerEnvironment::~ConcurrentTimerEnvironment() {
|
||||
_thread.quit();
|
||||
release();
|
||||
_thread.wait();
|
||||
QObject::disconnect(&_adjuster, &QObject::destroyed, nullptr, nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<TimerObject> ConcurrentTimerEnvironment::createTimer(
|
||||
Fn<void()> adjust) {
|
||||
return std::make_unique<TimerObject>(
|
||||
&_thread,
|
||||
&_adjuster,
|
||||
std::move(adjust));
|
||||
}
|
||||
|
||||
void ConcurrentTimerEnvironment::Adjust() {
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
if (Environment) {
|
||||
Environment->adjustTimers();
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentTimerEnvironment::adjustTimers() {
|
||||
QObject emitter;
|
||||
QObject::connect(
|
||||
&emitter,
|
||||
&QObject::destroyed,
|
||||
&_adjuster,
|
||||
&QObject::destroyed,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void ConcurrentTimerEnvironment::acquire() {
|
||||
Expects(Environment == nullptr);
|
||||
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
Environment = this;
|
||||
}
|
||||
|
||||
void ConcurrentTimerEnvironment::release() {
|
||||
Expects(Environment == this);
|
||||
|
||||
QMutexLocker lock(&EnvironmentMutex);
|
||||
Environment = nullptr;
|
||||
}
|
||||
|
||||
ConcurrentTimer::ConcurrentTimer(
|
||||
Fn<void(FnMut<void()>)> runner,
|
||||
Fn<void()> callback)
|
||||
: _runner(std::move(runner))
|
||||
, _object(createAdjuster())
|
||||
, _callback(std::move(callback))
|
||||
, _type(Qt::PreciseTimer)
|
||||
, _adjusted(false) {
|
||||
setRepeat(Repeat::Interval);
|
||||
}
|
||||
|
||||
Fn<void()> ConcurrentTimer::createAdjuster() {
|
||||
_guard = std::make_shared<bool>(true);
|
||||
return [=, runner = _runner, guard = std::weak_ptr<bool>(_guard)] {
|
||||
runner([=] {
|
||||
if (!guard.lock()) {
|
||||
return;
|
||||
}
|
||||
adjust();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
void ConcurrentTimer::start(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
Repeat repeat) {
|
||||
_type = type;
|
||||
setRepeat(repeat);
|
||||
_adjusted = false;
|
||||
setTimeout(timeout);
|
||||
|
||||
cancelAndSchedule(_timeout);
|
||||
_next = crl::now() + _timeout;
|
||||
}
|
||||
|
||||
void ConcurrentTimer::cancelAndSchedule(int timeout) {
|
||||
auto method = [
|
||||
=,
|
||||
runner = _runner,
|
||||
guard = _running.make_guard()
|
||||
]() mutable {
|
||||
if (!guard) {
|
||||
return;
|
||||
}
|
||||
runner([=, guard = std::move(guard)] {
|
||||
if (!guard) {
|
||||
return;
|
||||
}
|
||||
timerEvent();
|
||||
});
|
||||
};
|
||||
_object.call(timeout, _type, std::move(method));
|
||||
}
|
||||
|
||||
void ConcurrentTimer::timerEvent() {
|
||||
if (repeat() == Repeat::Interval) {
|
||||
if (_adjusted) {
|
||||
start(_timeout, _type, repeat());
|
||||
} else {
|
||||
_next = crl::now() + _timeout;
|
||||
}
|
||||
} else {
|
||||
cancel();
|
||||
}
|
||||
|
||||
if (_callback) {
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentTimer::cancel() {
|
||||
_running = {};
|
||||
if (isActive()) {
|
||||
_running = base::binary_guard();
|
||||
_object.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
crl::time ConcurrentTimer::remainingTime() const {
|
||||
if (!isActive()) {
|
||||
return -1;
|
||||
}
|
||||
const auto now = crl::now();
|
||||
return (_next > now) ? (_next - now) : crl::time(0);
|
||||
}
|
||||
|
||||
void ConcurrentTimer::adjust() {
|
||||
auto remaining = remainingTime();
|
||||
if (remaining >= 0) {
|
||||
cancelAndSchedule(remaining);
|
||||
_adjusted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentTimer::setTimeout(crl::time timeout) {
|
||||
Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
|
||||
|
||||
_timeout = static_cast<unsigned int>(timeout);
|
||||
}
|
||||
|
||||
int ConcurrentTimer::timeout() const {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/binary_guard.h"
|
||||
#include <crl/crl_time.h>
|
||||
#include <crl/crl_object_on_queue.h>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
|
||||
class TimerObject;
|
||||
|
||||
class TimerObjectWrap {
|
||||
public:
|
||||
explicit TimerObjectWrap(Fn<void()> adjust);
|
||||
~TimerObjectWrap();
|
||||
|
||||
void call(
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method);
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
void sendEvent(std::unique_ptr<QEvent> event);
|
||||
|
||||
std::unique_ptr<TimerObject> _value;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
class ConcurrentTimerEnvironment {
|
||||
public:
|
||||
ConcurrentTimerEnvironment();
|
||||
~ConcurrentTimerEnvironment();
|
||||
|
||||
std::unique_ptr<details::TimerObject> createTimer(Fn<void()> adjust);
|
||||
|
||||
static void Adjust();
|
||||
|
||||
private:
|
||||
void acquire();
|
||||
void release();
|
||||
void adjustTimers();
|
||||
|
||||
QThread _thread;
|
||||
QObject _adjuster;
|
||||
|
||||
};
|
||||
|
||||
class ConcurrentTimer {
|
||||
public:
|
||||
explicit ConcurrentTimer(
|
||||
Fn<void(FnMut<void()>)> runner,
|
||||
Fn<void()> callback = nullptr);
|
||||
|
||||
template <typename Object>
|
||||
explicit ConcurrentTimer(
|
||||
crl::weak_on_queue<Object> weak,
|
||||
Fn<void()> callback = nullptr);
|
||||
|
||||
static Qt::TimerType DefaultType(crl::time timeout) {
|
||||
constexpr auto kThreshold = crl::time(1000);
|
||||
return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer;
|
||||
}
|
||||
|
||||
void setCallback(Fn<void()> callback) {
|
||||
_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void callOnce(crl::time timeout) {
|
||||
callOnce(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callEach(crl::time timeout) {
|
||||
callEach(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callOnce(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::SingleShot);
|
||||
}
|
||||
|
||||
void callEach(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::Interval);
|
||||
}
|
||||
|
||||
bool isActive() const {
|
||||
return _running.alive();
|
||||
}
|
||||
|
||||
void cancel();
|
||||
crl::time remainingTime() const;
|
||||
|
||||
private:
|
||||
enum class Repeat : unsigned {
|
||||
Interval = 0,
|
||||
SingleShot = 1,
|
||||
};
|
||||
Fn<void()> createAdjuster();
|
||||
void start(crl::time timeout, Qt::TimerType type, Repeat repeat);
|
||||
void adjust();
|
||||
|
||||
void cancelAndSchedule(int timeout);
|
||||
|
||||
void setTimeout(crl::time timeout);
|
||||
int timeout() const;
|
||||
|
||||
void timerEvent();
|
||||
|
||||
void setRepeat(Repeat repeat) {
|
||||
_repeat = static_cast<unsigned>(repeat);
|
||||
}
|
||||
Repeat repeat() const {
|
||||
return static_cast<Repeat>(_repeat);
|
||||
}
|
||||
|
||||
Fn<void(FnMut<void()>)> _runner;
|
||||
std::shared_ptr<bool> _guard; // Must be before _object.
|
||||
details::TimerObjectWrap _object;
|
||||
Fn<void()> _callback;
|
||||
base::binary_guard _running;
|
||||
crl::time _next = 0;
|
||||
int _timeout = 0;
|
||||
|
||||
Qt::TimerType _type : 2;
|
||||
bool _adjusted : 1;
|
||||
unsigned _repeat : 1;
|
||||
|
||||
};
|
||||
|
||||
template <typename Object>
|
||||
ConcurrentTimer::ConcurrentTimer(
|
||||
crl::weak_on_queue<Object> weak,
|
||||
Fn<void()> callback)
|
||||
: ConcurrentTimer(weak.runner(), std::move(callback)) {
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/crc32hash.h"
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
class Crc32Table {
|
||||
public:
|
||||
Crc32Table() {
|
||||
auto poly = std::uint32_t(0x04c11db7);
|
||||
for (auto i = 0; i != 256; ++i) {
|
||||
_data[i] = reflect(i, 8) << 24;
|
||||
for (auto j = 0; j != 8; ++j) {
|
||||
_data[i] = (_data[i] << 1) ^ (_data[i] & (1 << 31) ? poly : 0);
|
||||
}
|
||||
_data[i] = reflect(_data[i], 32);
|
||||
}
|
||||
}
|
||||
|
||||
std::uint32_t operator[](int index) const {
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint32_t reflect(std::uint32_t val, char ch) {
|
||||
auto result = std::uint32_t(0);
|
||||
for (int i = 1; i < (ch + 1); ++i) {
|
||||
if (val & 1) {
|
||||
result |= 1 << (ch - i);
|
||||
}
|
||||
val >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::uint32_t _data[256];
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::int32_t crc32(const void *data, int len) {
|
||||
static const auto kTable = Crc32Table();
|
||||
|
||||
const auto buffer = static_cast<const std::uint8_t*>(data);
|
||||
|
||||
auto crc = std::uint32_t(0xffffffff);
|
||||
for (auto i = 0; i != len; ++i) {
|
||||
crc = (crc >> 8) ^ kTable[(crc & 0xFF) ^ buffer[i]];
|
||||
}
|
||||
|
||||
return static_cast<std::int32_t>(crc ^ 0xffffffff);
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace base {
|
||||
|
||||
std::int32_t crc32(const void *data, int len);
|
||||
|
||||
} // namespace base
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Enum>
|
||||
class enum_mask {
|
||||
using Type = std::uint32_t;
|
||||
|
||||
public:
|
||||
static_assert(static_cast<int>(Enum::kCount) <= 32, "We have only 32 bit.");
|
||||
|
||||
enum_mask() = default;
|
||||
enum_mask(Enum value) : _value(ToBit(value)) {
|
||||
}
|
||||
|
||||
enum_mask added(enum_mask other) const {
|
||||
auto result = *this;
|
||||
result.set(other);
|
||||
return result;
|
||||
}
|
||||
void set(enum_mask other) {
|
||||
_value |= other._value;
|
||||
}
|
||||
bool test(Enum value) const {
|
||||
return _value & ToBit(value);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return _value != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
inline static Type ToBit(Enum value) {
|
||||
return 1 << static_cast<Type>(value);
|
||||
}
|
||||
Type _value = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
|
@ -1,351 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename EnumType>
|
||||
class flags;
|
||||
|
||||
template <typename ExtendedEnum>
|
||||
struct extended_flags;
|
||||
|
||||
template <typename ExtendedEnum>
|
||||
using extended_flags_t = typename extended_flags<ExtendedEnum>::type;
|
||||
|
||||
namespace details {
|
||||
|
||||
struct flags_zero_helper_struct {
|
||||
};
|
||||
|
||||
using flags_zero_helper = void(base::details::flags_zero_helper_struct::*)();
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename Enum = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto extended_flag_convert(ExtendedEnum value) {
|
||||
return static_cast<Enum>(value);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename Enum = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto extended_flags_convert(ExtendedEnum value) {
|
||||
return flags<Enum>(extended_flag_convert(value));
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename EnumType>
|
||||
class flags {
|
||||
public:
|
||||
using Enum = EnumType;
|
||||
using Type = std::underlying_type_t<Enum>;
|
||||
|
||||
constexpr flags() = default;
|
||||
constexpr flags(details::flags_zero_helper) noexcept {
|
||||
}
|
||||
constexpr flags(Enum value) noexcept
|
||||
: _value(static_cast<Type>(value)) {
|
||||
}
|
||||
static constexpr flags from_raw(Type value) noexcept {
|
||||
return flags(static_cast<Enum>(value));
|
||||
}
|
||||
|
||||
constexpr auto value() const noexcept {
|
||||
return _value;
|
||||
}
|
||||
constexpr operator Type() const noexcept {
|
||||
return value();
|
||||
}
|
||||
|
||||
constexpr auto &operator|=(flags b) noexcept {
|
||||
_value |= b.value();
|
||||
return *this;
|
||||
}
|
||||
constexpr auto &operator&=(flags b) noexcept {
|
||||
_value &= b.value();
|
||||
return *this;
|
||||
}
|
||||
constexpr auto &operator^=(flags b) noexcept {
|
||||
_value ^= b.value();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator~() const noexcept {
|
||||
return from_raw(~value());
|
||||
}
|
||||
|
||||
constexpr auto operator|(flags b) const noexcept {
|
||||
return (flags(*this) |= b);
|
||||
}
|
||||
constexpr auto operator&(flags b) const noexcept {
|
||||
return (flags(*this) &= b);
|
||||
}
|
||||
constexpr auto operator^(flags b) const noexcept {
|
||||
return (flags(*this) ^= b);
|
||||
}
|
||||
|
||||
constexpr auto operator|(Enum b) const noexcept {
|
||||
return (flags(*this) |= b);
|
||||
}
|
||||
constexpr auto operator&(Enum b) const noexcept {
|
||||
return (flags(*this) &= b);
|
||||
}
|
||||
constexpr auto operator^(Enum b) const noexcept {
|
||||
return (flags(*this) ^= b);
|
||||
}
|
||||
|
||||
constexpr auto operator==(Enum b) const noexcept {
|
||||
return (value() == static_cast<Type>(b));
|
||||
}
|
||||
constexpr auto operator!=(Enum b) const noexcept {
|
||||
return !(*this == b);
|
||||
}
|
||||
constexpr auto operator<(Enum b) const noexcept {
|
||||
return value() < static_cast<Type>(b);
|
||||
}
|
||||
constexpr auto operator>(Enum b) const noexcept {
|
||||
return (b < *this);
|
||||
}
|
||||
constexpr auto operator<=(Enum b) const noexcept {
|
||||
return !(b < *this);
|
||||
}
|
||||
constexpr auto operator>=(Enum b) const noexcept {
|
||||
return !(*this < b);
|
||||
}
|
||||
|
||||
private:
|
||||
Type _value = 0;
|
||||
|
||||
};
|
||||
|
||||
template <typename Enum>
|
||||
constexpr auto make_flags(Enum value) noexcept {
|
||||
return flags<Enum>(value);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(Enum a, flags<Enum> b) noexcept {
|
||||
return b | a;
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator&(Enum a, flags<Enum> b) noexcept {
|
||||
return b & a;
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator^(Enum a, flags<Enum> b) noexcept {
|
||||
return b ^ a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a | details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return b | a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = extended_flags_t<ExtendedEnum>>
|
||||
inline constexpr auto operator&(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a & details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator&(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return b & a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = extended_flags_t<ExtendedEnum>>
|
||||
inline constexpr auto operator^(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a ^ details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator^(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return b ^ a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto &operator&=(flags<extended_flags_t<ExtendedEnum>> &a, ExtendedEnum b) {
|
||||
return (a &= details::extended_flags_convert(b));
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto &operator|=(flags<extended_flags_t<ExtendedEnum>> &a, ExtendedEnum b) {
|
||||
return (a |= details::extended_flags_convert(b));
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto &operator^=(flags<extended_flags_t<ExtendedEnum>> &a, ExtendedEnum b) {
|
||||
return (a ^= details::extended_flags_convert(b));
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator==(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a == details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator==(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return (b == a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator!=(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator!=(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator<(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return a < details::extended_flags_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator<(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return details::extended_flags_convert(a) < b;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator>(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return (b < a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator>(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return (b < a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator<=(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return !(b < a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator<=(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return !(b < a);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator>=(flags<extended_flags_t<ExtendedEnum>> a, ExtendedEnum b) {
|
||||
return !(a < b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator>=(ExtendedEnum a, flags<extended_flags_t<ExtendedEnum>> b) {
|
||||
return !(a < b);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator!(Enum a) noexcept {
|
||||
return !base::make_flags(a);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator~(Enum a) noexcept {
|
||||
return ~base::make_flags(a);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(Enum a, Enum b) noexcept {
|
||||
return base::make_flags(a) | b;
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(Enum a, base::details::flags_zero_helper) noexcept {
|
||||
return base::make_flags(a);
|
||||
}
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
inline constexpr auto operator|(base::details::flags_zero_helper, Enum b) noexcept {
|
||||
return base::make_flags(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, ExtendedEnum b) {
|
||||
return base::details::extended_flags_convert(a) | b;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, typename base::extended_flags<ExtendedEnum>::type b) {
|
||||
return base::details::extended_flags_convert(a) | b;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(typename base::extended_flags<ExtendedEnum>::type a, ExtendedEnum b) {
|
||||
return b | a;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(base::details::flags_zero_helper, ExtendedEnum b) {
|
||||
return 0 | base::details::extended_flag_convert(b);
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator|(ExtendedEnum a, base::details::flags_zero_helper) {
|
||||
return base::details::extended_flag_convert(a) | 0;
|
||||
}
|
||||
|
||||
template <typename ExtendedEnum,
|
||||
typename = typename base::extended_flags<ExtendedEnum>::type>
|
||||
inline constexpr auto operator~(ExtendedEnum b) {
|
||||
return ~base::details::extended_flags_convert(b);
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "base/flags.h"
|
||||
|
||||
namespace MethodNamespace {
|
||||
|
||||
template <typename Enum>
|
||||
void TestFlags(Enum a, Enum b, Enum c) {
|
||||
auto abc = a | b;
|
||||
abc |= c;
|
||||
auto test = abc != a;
|
||||
CHECK(abc != a);
|
||||
CHECK(abc != (a | b));
|
||||
CHECK((abc & a) == a);
|
||||
CHECK((abc & b) == b);
|
||||
CHECK((abc & c) == c);
|
||||
CHECK((abc & ~a) == (b | c));
|
||||
CHECK((abc & ~(b | c)) == a);
|
||||
CHECK((abc ^ a) == (abc & ~a));
|
||||
|
||||
auto another = a | b;
|
||||
another |= c;
|
||||
CHECK(abc == another);
|
||||
another &= ~b;
|
||||
CHECK(another == (a | c));
|
||||
another ^= a;
|
||||
CHECK(another == c);
|
||||
another = 0;
|
||||
another = nullptr;
|
||||
auto is_zero = ((another & abc) == 0);
|
||||
CHECK(is_zero);
|
||||
CHECK(!(another & abc));
|
||||
auto more = a | another;
|
||||
auto just = a | 0;
|
||||
CHECK(more == just);
|
||||
CHECK(just);
|
||||
}
|
||||
|
||||
} // namespace MethodNamespace
|
||||
|
||||
namespace FlagsNamespace {
|
||||
|
||||
enum class Flag : int {
|
||||
one = (1 << 0),
|
||||
two = (1 << 1),
|
||||
three = (1 << 2),
|
||||
};
|
||||
inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
||||
class Class {
|
||||
public:
|
||||
enum class Public : long {
|
||||
one = (1 << 2),
|
||||
two = (1 << 1),
|
||||
three = (1 << 0),
|
||||
};
|
||||
friend inline constexpr auto is_flag_type(Public) { return true; }
|
||||
|
||||
static void TestPrivate();
|
||||
|
||||
private:
|
||||
enum class Private : long {
|
||||
one = (1 << 0),
|
||||
two = (1 << 1),
|
||||
three = (1 << 2),
|
||||
};
|
||||
friend inline constexpr auto is_flag_type(Private) { return true; }
|
||||
|
||||
};
|
||||
|
||||
void Class::TestPrivate() {
|
||||
MethodNamespace::TestFlags(Private::one, Private::two, Private::three);
|
||||
}
|
||||
|
||||
} // namespace FlagsNamespace
|
||||
|
||||
namespace ExtendedNamespace {
|
||||
|
||||
enum class Flag : int {
|
||||
one = (1 << 3),
|
||||
two = (1 << 4),
|
||||
three = (1 << 5),
|
||||
};
|
||||
|
||||
} // namespace ExtendedNamespace
|
||||
|
||||
namespace base {
|
||||
|
||||
template<>
|
||||
struct extended_flags<ExtendedNamespace::Flag> {
|
||||
using type = FlagsNamespace::Flag;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
TEST_CASE("flags operators on scoped enums", "[flags]") {
|
||||
SECTION("testing non-member flags") {
|
||||
MethodNamespace::TestFlags(
|
||||
FlagsNamespace::Flag::one,
|
||||
FlagsNamespace::Flag::two,
|
||||
FlagsNamespace::Flag::three);
|
||||
}
|
||||
SECTION("testing public member flags") {
|
||||
MethodNamespace::TestFlags(
|
||||
FlagsNamespace::Class::Public::one,
|
||||
FlagsNamespace::Class::Public::two,
|
||||
FlagsNamespace::Class::Public::three);
|
||||
}
|
||||
SECTION("testing private member flags") {
|
||||
FlagsNamespace::Class::TestPrivate();
|
||||
}
|
||||
SECTION("testing extended flags") {
|
||||
MethodNamespace::TestFlags(
|
||||
ExtendedNamespace::Flag::one,
|
||||
ExtendedNamespace::Flag::two,
|
||||
ExtendedNamespace::Flag::three);
|
||||
|
||||
auto onetwo = FlagsNamespace::Flag::one | ExtendedNamespace::Flag::two;
|
||||
auto twoone = ExtendedNamespace::Flag::two | FlagsNamespace::Flag::one;
|
||||
CHECK(onetwo == twoone);
|
||||
}
|
||||
}
|
|
@ -1,917 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
#include "base/optional.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
template <
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename Compare = std::less<>>
|
||||
class flat_map;
|
||||
|
||||
template <
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename Compare = std::less<>>
|
||||
class flat_multi_map;
|
||||
|
||||
template <
|
||||
typename Me,
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename iterator_impl,
|
||||
typename pointer_impl,
|
||||
typename reference_impl>
|
||||
class flat_multi_map_iterator_base_impl;
|
||||
|
||||
template <typename Key, typename Value>
|
||||
struct flat_multi_map_pair_type {
|
||||
using first_type = const Key;
|
||||
using second_type = Value;
|
||||
|
||||
constexpr flat_multi_map_pair_type()
|
||||
: first()
|
||||
, second() {
|
||||
}
|
||||
|
||||
template <typename OtherKey, typename OtherValue>
|
||||
constexpr flat_multi_map_pair_type(OtherKey &&key, OtherValue &&value)
|
||||
: first(std::forward<OtherKey>(key))
|
||||
, second(std::forward<OtherValue>(value)) {
|
||||
}
|
||||
|
||||
flat_multi_map_pair_type(const flat_multi_map_pair_type &pair)
|
||||
: first(pair.first)
|
||||
, second(pair.second) {
|
||||
}
|
||||
|
||||
flat_multi_map_pair_type(flat_multi_map_pair_type &&pair)
|
||||
: first(std::move(const_cast<Key&>(pair.first)))
|
||||
, second(std::move(pair.second)) {
|
||||
}
|
||||
|
||||
flat_multi_map_pair_type &operator=(const flat_multi_map_pair_type&) = delete;
|
||||
flat_multi_map_pair_type &operator=(flat_multi_map_pair_type &&other) {
|
||||
const_cast<Key&>(first) = std::move(const_cast<Key&>(other.first));
|
||||
second = std::move(other.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(flat_multi_map_pair_type &other) {
|
||||
using std::swap;
|
||||
|
||||
if (this != &other) {
|
||||
std::swap(
|
||||
const_cast<Key&>(first),
|
||||
const_cast<Key&>(other.first));
|
||||
std::swap(second, other.second);
|
||||
}
|
||||
}
|
||||
|
||||
const Key first;
|
||||
Value second;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename Me,
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename iterator_impl,
|
||||
typename pointer_impl,
|
||||
typename reference_impl>
|
||||
class flat_multi_map_iterator_base_impl {
|
||||
public:
|
||||
using iterator_category = typename iterator_impl::iterator_category;
|
||||
|
||||
using pair_type = flat_multi_map_pair_type<Key, Type>;
|
||||
using value_type = pair_type;
|
||||
using difference_type = typename iterator_impl::difference_type;
|
||||
using pointer = pointer_impl;
|
||||
using reference = reference_impl;
|
||||
|
||||
flat_multi_map_iterator_base_impl(iterator_impl impl = iterator_impl())
|
||||
: _impl(impl) {
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
return *_impl;
|
||||
}
|
||||
pointer operator->() const {
|
||||
return std::addressof(**this);
|
||||
}
|
||||
Me &operator++() {
|
||||
++_impl;
|
||||
return static_cast<Me&>(*this);
|
||||
}
|
||||
Me operator++(int) {
|
||||
return _impl++;
|
||||
}
|
||||
Me &operator--() {
|
||||
--_impl;
|
||||
return static_cast<Me&>(*this);
|
||||
}
|
||||
Me operator--(int) {
|
||||
return _impl--;
|
||||
}
|
||||
Me &operator+=(difference_type offset) {
|
||||
_impl += offset;
|
||||
return static_cast<Me&>(*this);
|
||||
}
|
||||
Me operator+(difference_type offset) const {
|
||||
return _impl + offset;
|
||||
}
|
||||
Me &operator-=(difference_type offset) {
|
||||
_impl -= offset;
|
||||
return static_cast<Me&>(*this);
|
||||
}
|
||||
Me operator-(difference_type offset) const {
|
||||
return _impl - offset;
|
||||
}
|
||||
template <
|
||||
typename other_me,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
difference_type operator-(
|
||||
const flat_multi_map_iterator_base_impl<
|
||||
other_me,
|
||||
Key,
|
||||
Type,
|
||||
other_iterator_impl,
|
||||
other_pointer_impl,
|
||||
other_reference_impl> &right) const {
|
||||
return _impl - right._impl;
|
||||
}
|
||||
reference operator[](difference_type offset) const {
|
||||
return _impl[offset];
|
||||
}
|
||||
|
||||
template <
|
||||
typename other_me,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
bool operator==(
|
||||
const flat_multi_map_iterator_base_impl<
|
||||
other_me,
|
||||
Key,
|
||||
Type,
|
||||
other_iterator_impl,
|
||||
other_pointer_impl,
|
||||
other_reference_impl> &right) const {
|
||||
return _impl == right._impl;
|
||||
}
|
||||
template <
|
||||
typename other_me,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
bool operator!=(
|
||||
const flat_multi_map_iterator_base_impl<
|
||||
other_me,
|
||||
Key,
|
||||
Type,
|
||||
other_iterator_impl,
|
||||
other_pointer_impl,
|
||||
other_reference_impl> &right) const {
|
||||
return _impl != right._impl;
|
||||
}
|
||||
template <
|
||||
typename other_me,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
bool operator<(
|
||||
const flat_multi_map_iterator_base_impl<
|
||||
other_me,
|
||||
Key,
|
||||
Type,
|
||||
other_iterator_impl,
|
||||
other_pointer_impl,
|
||||
other_reference_impl> &right) const {
|
||||
return _impl < right._impl;
|
||||
}
|
||||
|
||||
private:
|
||||
iterator_impl _impl;
|
||||
|
||||
template <
|
||||
typename OtherKey,
|
||||
typename OtherType,
|
||||
typename OtherCompare>
|
||||
friend class flat_multi_map;
|
||||
|
||||
template <
|
||||
typename OtherMe,
|
||||
typename OtherKey,
|
||||
typename OtherType,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
friend class flat_multi_map_iterator_base_impl;
|
||||
|
||||
};
|
||||
|
||||
template <typename Key, typename Type, typename Compare>
|
||||
class flat_multi_map {
|
||||
public:
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
class reverse_iterator;
|
||||
class const_reverse_iterator;
|
||||
|
||||
private:
|
||||
using pair_type = flat_multi_map_pair_type<Key, Type>;
|
||||
using impl_t = std::deque<pair_type>;
|
||||
|
||||
using iterator_base = flat_multi_map_iterator_base_impl<
|
||||
iterator,
|
||||
Key,
|
||||
Type,
|
||||
typename impl_t::iterator,
|
||||
pair_type*,
|
||||
pair_type&>;
|
||||
using const_iterator_base = flat_multi_map_iterator_base_impl<
|
||||
const_iterator,
|
||||
Key,
|
||||
Type,
|
||||
typename impl_t::const_iterator,
|
||||
const pair_type*,
|
||||
const pair_type&>;
|
||||
using reverse_iterator_base = flat_multi_map_iterator_base_impl<
|
||||
reverse_iterator,
|
||||
Key,
|
||||
Type,
|
||||
typename impl_t::reverse_iterator,
|
||||
pair_type*,
|
||||
pair_type&>;
|
||||
using const_reverse_iterator_base = flat_multi_map_iterator_base_impl<
|
||||
const_reverse_iterator,
|
||||
Key,
|
||||
Type,
|
||||
typename impl_t::const_reverse_iterator,
|
||||
const pair_type*,
|
||||
const pair_type&>;
|
||||
|
||||
public:
|
||||
using value_type = pair_type;
|
||||
using size_type = typename impl_t::size_type;
|
||||
using difference_type = typename impl_t::difference_type;
|
||||
using pointer = pair_type*;
|
||||
using const_pointer = const pair_type*;
|
||||
using reference = pair_type&;
|
||||
using const_reference = const pair_type&;
|
||||
|
||||
class iterator : public iterator_base {
|
||||
public:
|
||||
using iterator_base::iterator_base;
|
||||
iterator() = default;
|
||||
iterator(const iterator_base &other) : iterator_base(other) {
|
||||
}
|
||||
friend class const_iterator;
|
||||
|
||||
};
|
||||
class const_iterator : public const_iterator_base {
|
||||
public:
|
||||
using const_iterator_base::const_iterator_base;
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator_base other) : const_iterator_base(other) {
|
||||
}
|
||||
const_iterator(const iterator &other) : const_iterator_base(other._impl) {
|
||||
}
|
||||
|
||||
};
|
||||
class reverse_iterator : public reverse_iterator_base {
|
||||
public:
|
||||
using reverse_iterator_base::reverse_iterator_base;
|
||||
reverse_iterator() = default;
|
||||
reverse_iterator(reverse_iterator_base other) : reverse_iterator_base(other) {
|
||||
}
|
||||
friend class const_reverse_iterator;
|
||||
|
||||
};
|
||||
class const_reverse_iterator : public const_reverse_iterator_base {
|
||||
public:
|
||||
using const_reverse_iterator_base::const_reverse_iterator_base;
|
||||
const_reverse_iterator() = default;
|
||||
const_reverse_iterator(const_reverse_iterator_base other) : const_reverse_iterator_base(other) {
|
||||
}
|
||||
const_reverse_iterator(const reverse_iterator &other) : const_reverse_iterator_base(other._impl) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
flat_multi_map() = default;
|
||||
flat_multi_map(const flat_multi_map &other) = default;
|
||||
flat_multi_map(flat_multi_map &&other) = default;
|
||||
flat_multi_map &operator=(const flat_multi_map &other) {
|
||||
auto copy = other;
|
||||
return (*this = std::move(copy));
|
||||
}
|
||||
flat_multi_map &operator=(flat_multi_map &&other) = default;
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
flat_multi_map(Iterator first, Iterator last)
|
||||
: _data(first, last) {
|
||||
std::sort(std::begin(impl()), std::end(impl()), compare());
|
||||
}
|
||||
|
||||
flat_multi_map(std::initializer_list<pair_type> iter)
|
||||
: flat_multi_map(iter.begin(), iter.end()) {
|
||||
}
|
||||
|
||||
size_type size() const {
|
||||
return impl().size();
|
||||
}
|
||||
bool empty() const {
|
||||
return impl().empty();
|
||||
}
|
||||
void clear() {
|
||||
impl().clear();
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return impl().begin();
|
||||
}
|
||||
iterator end() {
|
||||
return impl().end();
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return impl().begin();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return impl().end();
|
||||
}
|
||||
const_iterator cbegin() const {
|
||||
return impl().cbegin();
|
||||
}
|
||||
const_iterator cend() const {
|
||||
return impl().cend();
|
||||
}
|
||||
reverse_iterator rbegin() {
|
||||
return impl().rbegin();
|
||||
}
|
||||
reverse_iterator rend() {
|
||||
return impl().rend();
|
||||
}
|
||||
const_reverse_iterator rbegin() const {
|
||||
return impl().rbegin();
|
||||
}
|
||||
const_reverse_iterator rend() const {
|
||||
return impl().rend();
|
||||
}
|
||||
const_reverse_iterator crbegin() const {
|
||||
return impl().crbegin();
|
||||
}
|
||||
const_reverse_iterator crend() const {
|
||||
return impl().crend();
|
||||
}
|
||||
|
||||
reference front() {
|
||||
return *begin();
|
||||
}
|
||||
const_reference front() const {
|
||||
return *begin();
|
||||
}
|
||||
reference back() {
|
||||
return *(end() - 1);
|
||||
}
|
||||
const_reference back() const {
|
||||
return *(end() - 1);
|
||||
}
|
||||
|
||||
iterator insert(const value_type &value) {
|
||||
if (empty() || compare()(value.first, front().first)) {
|
||||
impl().push_front(value);
|
||||
return begin();
|
||||
} else if (!compare()(value.first, back().first)) {
|
||||
impl().push_back(value);
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value.first);
|
||||
return impl().insert(where, value);
|
||||
}
|
||||
iterator insert(value_type &&value) {
|
||||
if (empty() || compare()(value.first, front().first)) {
|
||||
impl().push_front(std::move(value));
|
||||
return begin();
|
||||
} else if (!compare()(value.first, back().first)) {
|
||||
impl().push_back(std::move(value));
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value.first);
|
||||
return impl().insert(where, std::move(value));
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator emplace(Args&&... args) {
|
||||
return insert(value_type(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
bool removeOne(const Key &key) {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return false;
|
||||
}
|
||||
auto where = getLowerBound(key);
|
||||
if (compare()(key, where->first)) {
|
||||
return false;
|
||||
}
|
||||
impl().erase(where);
|
||||
return true;
|
||||
}
|
||||
int removeAll(const Key &key) {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(key);
|
||||
if (range.first == range.second) {
|
||||
return 0;
|
||||
}
|
||||
const auto result = (range.second - range.first);
|
||||
impl().erase(range.first, range.second);
|
||||
return result;
|
||||
}
|
||||
|
||||
iterator erase(const_iterator where) {
|
||||
return impl().erase(where._impl);
|
||||
}
|
||||
iterator erase(const_iterator from, const_iterator till) {
|
||||
return impl().erase(from._impl, till._impl);
|
||||
}
|
||||
int erase(const Key &key) {
|
||||
return removeAll(key);
|
||||
}
|
||||
|
||||
iterator findFirst(const Key &key) {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(key);
|
||||
return compare()(key, where->first) ? impl().end() : where;
|
||||
}
|
||||
|
||||
const_iterator findFirst(const Key &key) const {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(key);
|
||||
return compare()(key, where->first) ? impl().end() : where;
|
||||
}
|
||||
|
||||
template <typename OtherKey>
|
||||
iterator findFirst(const OtherKey &key) {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(key);
|
||||
return compare()(key, where->first) ? impl().end() : where;
|
||||
}
|
||||
|
||||
template <typename OtherKey>
|
||||
const_iterator findFirst(const OtherKey &key) const {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(key);
|
||||
return compare()(key, where->first) ? impl().end() : where;
|
||||
}
|
||||
|
||||
bool contains(const Key &key) const {
|
||||
return findFirst(key) != end();
|
||||
}
|
||||
int count(const Key &key) const {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(key);
|
||||
return (range.second - range.first);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class flat_map<Key, Type, Compare>;
|
||||
|
||||
struct transparent_compare : Compare {
|
||||
inline constexpr const Compare &initial() const noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType1>, pair_type> &&
|
||||
!std::is_same_v<std::decay_t<OtherType2>, pair_type>>>
|
||||
inline constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const {
|
||||
return initial()(
|
||||
std::forward<OtherType1>(a),
|
||||
std::forward<OtherType2>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2>
|
||||
inline constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const -> std::enable_if_t<
|
||||
std::is_same_v<std::decay_t<OtherType1>, pair_type> &&
|
||||
std::is_same_v<std::decay_t<OtherType2>, pair_type>, bool> {
|
||||
return initial()(a.first, b.first);
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, pair_type>>>
|
||||
inline constexpr auto operator()(
|
||||
const pair_type &a,
|
||||
OtherType &&b) const {
|
||||
return operator()(a.first, std::forward<OtherType>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, pair_type>>>
|
||||
inline constexpr auto operator()(
|
||||
OtherType &&a,
|
||||
const pair_type &b) const {
|
||||
return operator()(std::forward<OtherType>(a), b.first);
|
||||
}
|
||||
|
||||
};
|
||||
struct Data : transparent_compare {
|
||||
template <typename ...Args>
|
||||
Data(Args &&...args)
|
||||
: elements(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
impl_t elements;
|
||||
};
|
||||
|
||||
Data _data;
|
||||
const transparent_compare &compare() const noexcept {
|
||||
return _data;
|
||||
}
|
||||
const impl_t &impl() const noexcept {
|
||||
return _data.elements;
|
||||
}
|
||||
impl_t &impl() noexcept {
|
||||
return _data.elements;
|
||||
}
|
||||
|
||||
template <typename OtherKey>
|
||||
typename impl_t::iterator getLowerBound(const OtherKey &key) {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherKey>
|
||||
typename impl_t::const_iterator getLowerBound(const OtherKey &key) const {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherKey>
|
||||
typename impl_t::iterator getUpperBound(const OtherKey &key) {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherKey>
|
||||
typename impl_t::const_iterator getUpperBound(const OtherKey &key) const {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherKey>
|
||||
std::pair<
|
||||
typename impl_t::iterator,
|
||||
typename impl_t::iterator
|
||||
> getEqualRange(const OtherKey &key) {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
template <typename OtherKey>
|
||||
std::pair<
|
||||
typename impl_t::const_iterator,
|
||||
typename impl_t::const_iterator
|
||||
> getEqualRange(const OtherKey &key) const {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Key, typename Type, typename Compare>
|
||||
class flat_map : private flat_multi_map<Key, Type, Compare> {
|
||||
using parent = flat_multi_map<Key, Type, Compare>;
|
||||
using pair_type = typename parent::pair_type;
|
||||
|
||||
public:
|
||||
using value_type = typename parent::value_type;
|
||||
using size_type = typename parent::size_type;
|
||||
using difference_type = typename parent::difference_type;
|
||||
using pointer = typename parent::pointer;
|
||||
using const_pointer = typename parent::const_pointer;
|
||||
using reference = typename parent::reference;
|
||||
using const_reference = typename parent::const_reference;
|
||||
using iterator = typename parent::iterator;
|
||||
using const_iterator = typename parent::const_iterator;
|
||||
using reverse_iterator = typename parent::reverse_iterator;
|
||||
using const_reverse_iterator = typename parent::const_reverse_iterator;
|
||||
|
||||
flat_map() = default;
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category
|
||||
>
|
||||
flat_map(Iterator first, Iterator last) : parent(first, last) {
|
||||
finalize();
|
||||
}
|
||||
|
||||
flat_map(std::initializer_list<pair_type> iter) : parent(iter.begin(), iter.end()) {
|
||||
finalize();
|
||||
}
|
||||
|
||||
using parent::parent;
|
||||
using parent::size;
|
||||
using parent::empty;
|
||||
using parent::clear;
|
||||
using parent::begin;
|
||||
using parent::end;
|
||||
using parent::cbegin;
|
||||
using parent::cend;
|
||||
using parent::rbegin;
|
||||
using parent::rend;
|
||||
using parent::crbegin;
|
||||
using parent::crend;
|
||||
using parent::front;
|
||||
using parent::back;
|
||||
using parent::erase;
|
||||
using parent::contains;
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type &value) {
|
||||
if (this->empty() || this->compare()(value.first, this->front().first)) {
|
||||
this->impl().push_front(value);
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, value.first)) {
|
||||
this->impl().push_back(value);
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(value.first);
|
||||
if (this->compare()(value.first, where->first)) {
|
||||
return { this->impl().insert(where, value), true };
|
||||
}
|
||||
return { where, false };
|
||||
}
|
||||
std::pair<iterator, bool> insert(value_type &&value) {
|
||||
if (this->empty() || this->compare()(value.first, this->front().first)) {
|
||||
this->impl().push_front(std::move(value));
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, value.first)) {
|
||||
this->impl().push_back(std::move(value));
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(value.first);
|
||||
if (this->compare()(value.first, where->first)) {
|
||||
return { this->impl().insert(where, std::move(value)), true };
|
||||
}
|
||||
return { where, false };
|
||||
}
|
||||
std::pair<iterator, bool> insert_or_assign(
|
||||
const Key &key,
|
||||
const Type &value) {
|
||||
if (this->empty() || this->compare()(key, this->front().first)) {
|
||||
this->impl().emplace_front(key, value);
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, key)) {
|
||||
this->impl().emplace_back(key, value);
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(key);
|
||||
if (this->compare()(key, where->first)) {
|
||||
return { this->impl().insert(where, value_type(key, value)), true };
|
||||
}
|
||||
where->second = value;
|
||||
return { where, false };
|
||||
}
|
||||
std::pair<iterator, bool> insert_or_assign(
|
||||
const Key &key,
|
||||
Type &&value) {
|
||||
if (this->empty() || this->compare()(key, this->front().first)) {
|
||||
this->impl().emplace_front(key, std::move(value));
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, key)) {
|
||||
this->impl().emplace_back(key, std::move(value));
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(key);
|
||||
if (this->compare()(key, where->first)) {
|
||||
return { this->impl().insert(where, value_type(key, std::move(value))), true };
|
||||
}
|
||||
where->second = std::move(value);
|
||||
return { where, false };
|
||||
}
|
||||
template <typename OtherKey, typename... Args>
|
||||
std::pair<iterator, bool> emplace(
|
||||
OtherKey &&key,
|
||||
Args&&... args) {
|
||||
return this->insert(value_type(
|
||||
std::forward<OtherKey>(key),
|
||||
Type(std::forward<Args>(args)...)));
|
||||
}
|
||||
template <typename... Args>
|
||||
std::pair<iterator, bool> emplace_or_assign(
|
||||
const Key &key,
|
||||
Args&&... args) {
|
||||
return this->insert_or_assign(
|
||||
key,
|
||||
Type(std::forward<Args>(args)...));
|
||||
}
|
||||
template <typename... Args>
|
||||
std::pair<iterator, bool> try_emplace(
|
||||
const Key &key,
|
||||
Args&&... args) {
|
||||
if (this->empty() || this->compare()(key, this->front().first)) {
|
||||
this->impl().push_front(value_type(
|
||||
key,
|
||||
Type(std::forward<Args>(args)...)));
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, key)) {
|
||||
this->impl().push_back(value_type(
|
||||
key,
|
||||
Type(std::forward<Args>(args)...)));
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(key);
|
||||
if (this->compare()(key, where->first)) {
|
||||
return {
|
||||
this->impl().insert(
|
||||
where,
|
||||
value_type(
|
||||
key,
|
||||
Type(std::forward<Args>(args)...))),
|
||||
true
|
||||
};
|
||||
}
|
||||
return { where, false };
|
||||
}
|
||||
|
||||
bool remove(const Key &key) {
|
||||
return this->removeOne(key);
|
||||
}
|
||||
|
||||
iterator find(const Key &key) {
|
||||
return this->findFirst(key);
|
||||
}
|
||||
const_iterator find(const Key &key) const {
|
||||
return this->findFirst(key);
|
||||
}
|
||||
template <typename OtherKey>
|
||||
iterator find(const OtherKey &key) {
|
||||
return this->template findFirst<OtherKey>(key);
|
||||
}
|
||||
template <typename OtherKey>
|
||||
const_iterator find(const OtherKey &key) const {
|
||||
return this->template findFirst<OtherKey>(key);
|
||||
}
|
||||
|
||||
Type &operator[](const Key &key) {
|
||||
if (this->empty() || this->compare()(key, this->front().first)) {
|
||||
this->impl().push_front({ key, Type() });
|
||||
return this->front().second;
|
||||
} else if (this->compare()(this->back().first, key)) {
|
||||
this->impl().push_back({ key, Type() });
|
||||
return this->back().second;
|
||||
}
|
||||
auto where = this->getLowerBound(key);
|
||||
if (this->compare()(key, where->first)) {
|
||||
return this->impl().insert(where, { key, Type() })->second;
|
||||
}
|
||||
return where->second;
|
||||
}
|
||||
|
||||
std::optional<Type> take(const Key &key) {
|
||||
auto it = find(key);
|
||||
if (it == this->end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto result = std::move(it->second);
|
||||
this->erase(it);
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
private:
|
||||
void finalize() {
|
||||
this->impl().erase(
|
||||
std::unique(
|
||||
std::begin(this->impl()),
|
||||
std::end(this->impl()),
|
||||
[&](auto &&a, auto &&b) {
|
||||
return !this->compare()(a, b);
|
||||
}
|
||||
),
|
||||
std::end(this->impl()));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
// Structured bindings support.
|
||||
namespace std {
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class tuple_size<base::flat_multi_map_pair_type<Key, Value>>
|
||||
: public integral_constant<size_t, 2> {
|
||||
};
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class tuple_element<0, base::flat_multi_map_pair_type<Key, Value>> {
|
||||
public:
|
||||
using type = const Key;
|
||||
};
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class tuple_element<1, base::flat_multi_map_pair_type<Key, Value>> {
|
||||
public:
|
||||
using type = Value;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
// Structured bindings support.
|
||||
namespace base {
|
||||
namespace details {
|
||||
|
||||
template <std::size_t N, typename Key, typename Value>
|
||||
using flat_multi_map_pair_element = std::tuple_element_t<
|
||||
N,
|
||||
flat_multi_map_pair_type<Key, Value>>;
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <std::size_t N, typename Key, typename Value>
|
||||
auto get(base::flat_multi_map_pair_type<Key, Value> &value)
|
||||
-> details::flat_multi_map_pair_element<N, Key, Value> & {
|
||||
if constexpr (N == 0) {
|
||||
return value.first;
|
||||
} else {
|
||||
return value.second;
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t N, typename Key, typename Value>
|
||||
auto get(const base::flat_multi_map_pair_type<Key, Value> &value)
|
||||
-> const details::flat_multi_map_pair_element<N, Key, Value> & {
|
||||
if constexpr (N == 0) {
|
||||
return value.first;
|
||||
} else {
|
||||
return value.second;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "base/flat_map.h"
|
||||
#include <string>
|
||||
|
||||
struct int_wrap {
|
||||
int value;
|
||||
};
|
||||
struct int_wrap_comparator {
|
||||
inline bool operator()(const int_wrap &a, const int_wrap &b) const {
|
||||
return a.value < b.value;
|
||||
}
|
||||
};
|
||||
|
||||
using namespace std;
|
||||
|
||||
TEST_CASE("flat_maps should keep items sorted by key", "[flat_map]") {
|
||||
base::flat_map<int, string> v;
|
||||
v.emplace(0, "a");
|
||||
v.emplace(5, "b");
|
||||
v.emplace(4, "d");
|
||||
v.emplace(2, "e");
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(prev->first < i->first);
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.emplace(3, "c");
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find(3) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("simple flat_maps tests", "[flat_map]") {
|
||||
SECTION("copy constructor") {
|
||||
base::flat_map<int, string> v;
|
||||
v.emplace(0, "a");
|
||||
v.emplace(2, "b");
|
||||
auto u = v;
|
||||
REQUIRE(u.size() == 2);
|
||||
REQUIRE(u.find(0) == u.begin());
|
||||
REQUIRE(u.find(2) == u.end() - 1);
|
||||
}
|
||||
SECTION("assignment") {
|
||||
base::flat_map<int, string> v, u;
|
||||
v.emplace(0, "a");
|
||||
v.emplace(2, "b");
|
||||
u = v;
|
||||
REQUIRE(u.size() == 2);
|
||||
REQUIRE(u.find(0) == u.begin());
|
||||
REQUIRE(u.find(2) == u.end() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("flat_maps custom comparator", "[flat_map]") {
|
||||
base::flat_map<int_wrap, string, int_wrap_comparator> v;
|
||||
v.emplace(int_wrap{ 0 }, "a");
|
||||
v.emplace(int_wrap{ 5 }, "b");
|
||||
v.emplace(int_wrap{ 4 }, "d");
|
||||
v.emplace(int_wrap{ 2 }, "e");
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(int_wrap_comparator()(prev->first, i->first));
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.emplace(int_wrap{ 3 }, "c");
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find({ 3 }) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("flat_maps structured bindings", "[flat_map]") {
|
||||
base::flat_map<int, std::unique_ptr<double>> v;
|
||||
v.emplace(0, std::make_unique<double>(0.));
|
||||
v.emplace(1, std::make_unique<double>(1.));
|
||||
|
||||
SECTION("structred binded range-based for loop") {
|
||||
for (const auto &[key, value] : v) {
|
||||
REQUIRE(key == int(std::round(*value)));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("non-const structured binded range-based for loop") {
|
||||
base::flat_map<int, int> second = {
|
||||
{ 1, 1 },
|
||||
{ 2, 2 },
|
||||
{ 2, 3 },
|
||||
{ 3, 3 },
|
||||
};
|
||||
REQUIRE(second.size() == 3);
|
||||
//for (auto [a, b] : second) { // #MSVC Bug, reported
|
||||
// REQUIRE(a == b);
|
||||
//}
|
||||
for (const auto [a, b] : second) {
|
||||
REQUIRE(a == b);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,720 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
|
||||
namespace base {
|
||||
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
template <typename Type, typename Compare = std::less<>>
|
||||
class flat_set;
|
||||
|
||||
template <typename Type, typename Compare = std::less<>>
|
||||
class flat_multi_set;
|
||||
|
||||
template <typename Type, typename iterator_impl>
|
||||
class flat_multi_set_iterator_impl;
|
||||
|
||||
template <typename Type, typename iterator_impl>
|
||||
class flat_multi_set_iterator_impl {
|
||||
public:
|
||||
using iterator_category = typename iterator_impl::iterator_category;
|
||||
|
||||
using value_type = Type;
|
||||
using difference_type = typename iterator_impl::difference_type;
|
||||
using pointer = const Type*;
|
||||
using reference = const Type&;
|
||||
|
||||
flat_multi_set_iterator_impl(
|
||||
iterator_impl impl = iterator_impl())
|
||||
: _impl(impl) {
|
||||
}
|
||||
template <typename other_iterator_impl>
|
||||
flat_multi_set_iterator_impl(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &other)
|
||||
: _impl(other._impl) {
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
return *_impl;
|
||||
}
|
||||
pointer operator->() const {
|
||||
return std::addressof(**this);
|
||||
}
|
||||
flat_multi_set_iterator_impl &operator++() {
|
||||
++_impl;
|
||||
return *this;
|
||||
}
|
||||
flat_multi_set_iterator_impl operator++(int) {
|
||||
return _impl++;
|
||||
}
|
||||
flat_multi_set_iterator_impl &operator--() {
|
||||
--_impl;
|
||||
return *this;
|
||||
}
|
||||
flat_multi_set_iterator_impl operator--(int) {
|
||||
return _impl--;
|
||||
}
|
||||
flat_multi_set_iterator_impl &operator+=(difference_type offset) {
|
||||
_impl += offset;
|
||||
return *this;
|
||||
}
|
||||
flat_multi_set_iterator_impl operator+(difference_type offset) const {
|
||||
return _impl + offset;
|
||||
}
|
||||
flat_multi_set_iterator_impl &operator-=(difference_type offset) {
|
||||
_impl -= offset;
|
||||
return *this;
|
||||
}
|
||||
flat_multi_set_iterator_impl operator-(difference_type offset) const {
|
||||
return _impl - offset;
|
||||
}
|
||||
template <typename other_iterator_impl>
|
||||
difference_type operator-(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const {
|
||||
return _impl - right._impl;
|
||||
}
|
||||
reference operator[](difference_type offset) const {
|
||||
return _impl[offset];
|
||||
}
|
||||
|
||||
template <typename other_iterator_impl>
|
||||
bool operator==(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const {
|
||||
return _impl == right._impl;
|
||||
}
|
||||
template <typename other_iterator_impl>
|
||||
bool operator!=(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const {
|
||||
return _impl != right._impl;
|
||||
}
|
||||
template <typename other_iterator_impl>
|
||||
bool operator<(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const {
|
||||
return _impl < right._impl;
|
||||
}
|
||||
|
||||
private:
|
||||
iterator_impl _impl;
|
||||
|
||||
template <typename OtherType, typename OtherCompare>
|
||||
friend class flat_multi_set;
|
||||
|
||||
template <typename OtherType, typename OtherCompare>
|
||||
friend class flat_set;
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename other_iterator_impl>
|
||||
friend class flat_multi_set_iterator_impl;
|
||||
|
||||
Type &wrapped() {
|
||||
return _impl->wrapped();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class flat_multi_set_const_wrap {
|
||||
public:
|
||||
constexpr flat_multi_set_const_wrap(const Type &value)
|
||||
: _value(value) {
|
||||
}
|
||||
constexpr flat_multi_set_const_wrap(Type &&value)
|
||||
: _value(std::move(value)) {
|
||||
}
|
||||
inline constexpr operator const Type&() const {
|
||||
return _value;
|
||||
}
|
||||
constexpr Type &wrapped() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
private:
|
||||
Type _value;
|
||||
|
||||
};
|
||||
|
||||
template <typename Type, typename Compare>
|
||||
class flat_multi_set {
|
||||
using const_wrap = flat_multi_set_const_wrap<Type>;
|
||||
using impl_t = std::deque<const_wrap>;
|
||||
|
||||
public:
|
||||
using value_type = Type;
|
||||
using size_type = typename impl_t::size_type;
|
||||
using difference_type = typename impl_t::difference_type;
|
||||
using pointer = const Type*;
|
||||
using reference = const Type&;
|
||||
|
||||
using iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::iterator>;
|
||||
using const_iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::const_iterator>;
|
||||
using reverse_iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::reverse_iterator>;
|
||||
using const_reverse_iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::const_reverse_iterator>;
|
||||
|
||||
flat_multi_set() = default;
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
flat_multi_set(Iterator first, Iterator last)
|
||||
: _data(first, last) {
|
||||
std::sort(std::begin(impl()), std::end(impl()), compare());
|
||||
}
|
||||
|
||||
flat_multi_set(std::initializer_list<Type> iter)
|
||||
: flat_multi_set(iter.begin(), iter.end()) {
|
||||
}
|
||||
|
||||
size_type size() const {
|
||||
return impl().size();
|
||||
}
|
||||
bool empty() const {
|
||||
return impl().empty();
|
||||
}
|
||||
void clear() {
|
||||
impl().clear();
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return impl().begin();
|
||||
}
|
||||
iterator end() {
|
||||
return impl().end();
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return impl().begin();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return impl().end();
|
||||
}
|
||||
const_iterator cbegin() const {
|
||||
return impl().cbegin();
|
||||
}
|
||||
const_iterator cend() const {
|
||||
return impl().cend();
|
||||
}
|
||||
reverse_iterator rbegin() {
|
||||
return impl().rbegin();
|
||||
}
|
||||
reverse_iterator rend() {
|
||||
return impl().rend();
|
||||
}
|
||||
const_reverse_iterator rbegin() const {
|
||||
return impl().rbegin();
|
||||
}
|
||||
const_reverse_iterator rend() const {
|
||||
return impl().rend();
|
||||
}
|
||||
const_reverse_iterator crbegin() const {
|
||||
return impl().crbegin();
|
||||
}
|
||||
const_reverse_iterator crend() const {
|
||||
return impl().crend();
|
||||
}
|
||||
|
||||
reference front() const {
|
||||
return *begin();
|
||||
}
|
||||
reference back() const {
|
||||
return *(end() - 1);
|
||||
}
|
||||
|
||||
iterator insert(const Type &value) {
|
||||
if (empty() || compare()(value, front())) {
|
||||
impl().push_front(value);
|
||||
return begin();
|
||||
} else if (!compare()(value, back())) {
|
||||
impl().push_back(value);
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value);
|
||||
return impl().insert(where, value);
|
||||
}
|
||||
iterator insert(Type &&value) {
|
||||
if (empty() || compare()(value, front())) {
|
||||
impl().push_front(std::move(value));
|
||||
return begin();
|
||||
} else if (!compare()(value, back())) {
|
||||
impl().push_back(std::move(value));
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value);
|
||||
return impl().insert(where, std::move(value));
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator emplace(Args&&... args) {
|
||||
return insert(Type(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
bool removeOne(const Type &value) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return false;
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
if (compare()(value, *where)) {
|
||||
return false;
|
||||
}
|
||||
impl().erase(where);
|
||||
return true;
|
||||
}
|
||||
int removeAll(const Type &value) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(value);
|
||||
if (range.first == range.second) {
|
||||
return 0;
|
||||
}
|
||||
const auto result = (range.second - range.first);
|
||||
impl().erase(range.first, range.second);
|
||||
return result;
|
||||
}
|
||||
|
||||
iterator erase(const_iterator where) {
|
||||
return impl().erase(where._impl);
|
||||
}
|
||||
iterator erase(const_iterator from, const_iterator till) {
|
||||
return impl().erase(from._impl, till._impl);
|
||||
}
|
||||
int erase(const Type &value) {
|
||||
return removeAll(value);
|
||||
}
|
||||
|
||||
iterator findFirst(const Type &value) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
|
||||
const_iterator findFirst(const Type &value) const {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
iterator findFirst(const OtherType &value) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
const_iterator findFirst(const OtherType &value) const {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
|
||||
bool contains(const Type &value) const {
|
||||
return findFirst(value) != end();
|
||||
}
|
||||
int count(const Type &value) const {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(value);
|
||||
return (range.second - range.first);
|
||||
}
|
||||
|
||||
template <typename Action>
|
||||
auto modify(iterator which, Action action) {
|
||||
auto result = action(which.wrapped());
|
||||
for (auto i = which + 1, e = end(); i != e; ++i) {
|
||||
if (compare()(*i, *which)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto i = which, b = begin(); i != b;) {
|
||||
--i;
|
||||
if (compare()(*which, *i)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
void merge(Iterator first, Iterator last) {
|
||||
impl().insert(impl().end(), first, last);
|
||||
std::sort(std::begin(impl()), std::end(impl()), compare());
|
||||
}
|
||||
|
||||
void merge(const flat_multi_set<Type, Compare> &other) {
|
||||
merge(other.begin(), other.end());
|
||||
}
|
||||
|
||||
void merge(std::initializer_list<Type> list) {
|
||||
merge(list.begin(), list.end());
|
||||
}
|
||||
|
||||
private:
|
||||
friend class flat_set<Type, Compare>;
|
||||
|
||||
struct transparent_compare : Compare {
|
||||
inline constexpr const Compare &initial() const noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType1>, const_wrap> &&
|
||||
!std::is_same_v<std::decay_t<OtherType2>, const_wrap>>>
|
||||
inline constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const {
|
||||
return initial()(
|
||||
std::forward<OtherType1>(a),
|
||||
std::forward<OtherType2>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2>
|
||||
inline constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const -> std::enable_if_t<
|
||||
std::is_same_v<std::decay_t<OtherType1>, const_wrap> &&
|
||||
std::is_same_v<std::decay_t<OtherType2>, const_wrap>, bool> {
|
||||
return initial()(
|
||||
static_cast<const Type&>(a),
|
||||
static_cast<const Type&>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, const_wrap>>>
|
||||
inline constexpr auto operator()(
|
||||
const const_wrap &a,
|
||||
OtherType &&b) const {
|
||||
return initial()(
|
||||
static_cast<const Type&>(a),
|
||||
std::forward<OtherType>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, const_wrap>>>
|
||||
inline constexpr auto operator()(
|
||||
OtherType &&a,
|
||||
const const_wrap &b) const {
|
||||
return initial()(
|
||||
std::forward<OtherType>(a),
|
||||
static_cast<const Type&>(b));
|
||||
}
|
||||
|
||||
};
|
||||
struct Data : transparent_compare {
|
||||
template <typename ...Args>
|
||||
Data(Args &&...args)
|
||||
: elements(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
impl_t elements;
|
||||
};
|
||||
|
||||
Data _data;
|
||||
const transparent_compare &compare() const {
|
||||
return _data;
|
||||
}
|
||||
const impl_t &impl() const {
|
||||
return _data.elements;
|
||||
}
|
||||
impl_t &impl() {
|
||||
return _data.elements;
|
||||
}
|
||||
|
||||
typename impl_t::iterator getLowerBound(const Type &value) {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
typename impl_t::const_iterator getLowerBound(const Type &value) const {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
typename impl_t::iterator getLowerBound(const OtherType &value) {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
typename impl_t::const_iterator getLowerBound(const OtherType &value) const {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
typename impl_t::iterator getUpperBound(const Type &value) {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
typename impl_t::const_iterator getUpperBound(const Type &value) const {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
std::pair<
|
||||
typename impl_t::iterator,
|
||||
typename impl_t::iterator
|
||||
> getEqualRange(const Type &value) {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
std::pair<
|
||||
typename impl_t::const_iterator,
|
||||
typename impl_t::const_iterator
|
||||
> getEqualRange(const Type &value) const {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Type, typename Compare>
|
||||
class flat_set : private flat_multi_set<Type, Compare> {
|
||||
using parent = flat_multi_set<Type, Compare>;
|
||||
|
||||
public:
|
||||
using iterator = typename parent::iterator;
|
||||
using const_iterator = typename parent::const_iterator;
|
||||
using reverse_iterator = typename parent::reverse_iterator;
|
||||
using const_reverse_iterator = typename parent::const_reverse_iterator;
|
||||
using value_type = typename parent::value_type;
|
||||
using size_type = typename parent::size_type;
|
||||
using difference_type = typename parent::difference_type;
|
||||
using pointer = typename parent::pointer;
|
||||
using reference = typename parent::reference;
|
||||
|
||||
flat_set() = default;
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category
|
||||
>
|
||||
flat_set(Iterator first, Iterator last) : parent(first, last) {
|
||||
finalize();
|
||||
}
|
||||
|
||||
flat_set(std::initializer_list<Type> iter) : parent(iter.begin(), iter.end()) {
|
||||
finalize();
|
||||
}
|
||||
|
||||
using parent::parent;
|
||||
using parent::size;
|
||||
using parent::empty;
|
||||
using parent::clear;
|
||||
using parent::begin;
|
||||
using parent::end;
|
||||
using parent::cbegin;
|
||||
using parent::cend;
|
||||
using parent::rbegin;
|
||||
using parent::rend;
|
||||
using parent::crbegin;
|
||||
using parent::crend;
|
||||
using parent::front;
|
||||
using parent::back;
|
||||
using parent::contains;
|
||||
using parent::erase;
|
||||
|
||||
std::pair<iterator, bool> insert(const Type &value) {
|
||||
if (this->empty() || this->compare()(value, this->front())) {
|
||||
this->impl().push_front(value);
|
||||
return std::make_pair(this->begin(), true);
|
||||
} else if (this->compare()(this->back(), value)) {
|
||||
this->impl().push_back(value);
|
||||
return std::make_pair(this->end() - 1, true);
|
||||
}
|
||||
auto where = this->getLowerBound(value);
|
||||
if (this->compare()(value, *where)) {
|
||||
return std::make_pair(this->impl().insert(where, value), true);
|
||||
}
|
||||
return std::make_pair(where, false);
|
||||
}
|
||||
std::pair<iterator, bool> insert(Type &&value) {
|
||||
if (this->empty() || this->compare()(value, this->front())) {
|
||||
this->impl().push_front(std::move(value));
|
||||
return std::make_pair(this->begin(), true);
|
||||
} else if (this->compare()(this->back(), value)) {
|
||||
this->impl().push_back(std::move(value));
|
||||
return std::make_pair(this->end() - 1, true);
|
||||
}
|
||||
auto where = this->getLowerBound(value);
|
||||
if (this->compare()(value, *where)) {
|
||||
return std::make_pair(
|
||||
this->impl().insert(where, std::move(value)),
|
||||
true);
|
||||
}
|
||||
return std::make_pair(where, false);
|
||||
}
|
||||
template <typename... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return this->insert(Type(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
bool remove(const Type &value) {
|
||||
return this->removeOne(value);
|
||||
}
|
||||
|
||||
iterator find(const Type &value) {
|
||||
return this->findFirst(value);
|
||||
}
|
||||
const_iterator find(const Type &value) const {
|
||||
return this->findFirst(value);
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
iterator find(const OtherType &value) {
|
||||
return this->findFirst(value);
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
const_iterator find(const OtherType &value) const {
|
||||
return this->findFirst(value);
|
||||
}
|
||||
|
||||
template <typename Action>
|
||||
void modify(iterator which, Action action) {
|
||||
action(which.wrapped());
|
||||
for (auto i = iterator(which + 1), e = end(); i != e; ++i) {
|
||||
if (this->compare()(*i, *which)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else if (!this->compare()(*which, *i)) {
|
||||
erase(which);
|
||||
return;
|
||||
} else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto i = which, b = begin(); i != b;) {
|
||||
--i;
|
||||
if (this->compare()(*which, *i)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else if (!this->compare()(*i, *which)) {
|
||||
erase(which);
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
void merge(Iterator first, Iterator last) {
|
||||
parent::merge(first, last);
|
||||
finalize();
|
||||
}
|
||||
|
||||
void merge(const flat_multi_set<Type, Compare> &other) {
|
||||
merge(other.begin(), other.end());
|
||||
}
|
||||
|
||||
void merge(std::initializer_list<Type> list) {
|
||||
merge(list.begin(), list.end());
|
||||
}
|
||||
|
||||
private:
|
||||
void finalize() {
|
||||
this->impl().erase(
|
||||
std::unique(
|
||||
std::begin(this->impl()),
|
||||
std::end(this->impl()),
|
||||
[&](auto &&a, auto &&b) {
|
||||
return !this->compare()(a, b);
|
||||
}
|
||||
),
|
||||
std::end(this->impl()));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "base/flat_set.h"
|
||||
|
||||
struct int_wrap {
|
||||
int value;
|
||||
};
|
||||
struct int_wrap_comparator {
|
||||
using is_transparent = void;
|
||||
inline bool operator()(const int &a, const int_wrap &b) const {
|
||||
return a < b.value;
|
||||
}
|
||||
inline bool operator()(const int_wrap &a, const int_wrap &b) const {
|
||||
return a.value < b.value;
|
||||
}
|
||||
inline bool operator()(const int_wrap &a, const int &b) const {
|
||||
return a.value < b;
|
||||
}
|
||||
inline bool operator()(const int &a, const int &b) const {
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("flat_sets should keep items sorted", "[flat_set]") {
|
||||
|
||||
base::flat_set<int> v;
|
||||
v.insert(0);
|
||||
v.insert(5);
|
||||
v.insert(4);
|
||||
v.insert(2);
|
||||
|
||||
REQUIRE(v.contains(4));
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(*prev < *i);
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.insert(3);
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find(3) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("flat_sets with custom comparators", "[flat_set]") {
|
||||
base::flat_set<int_wrap, int_wrap_comparator> v;
|
||||
v.insert({ 0 });
|
||||
v.insert({ 5 });
|
||||
v.insert({ 4 });
|
||||
v.insert({ 2 });
|
||||
|
||||
REQUIRE(v.find(4) != v.end());
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(prev->value < i->value);
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.insert({ 3 });
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find(3) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
namespace functors {
|
||||
|
||||
struct abs_helper {
|
||||
template <
|
||||
typename Type,
|
||||
typename = decltype(0 < std::declval<Type>()),
|
||||
typename = decltype(-std::declval<Type>())>
|
||||
constexpr Type operator()(Type value) const {
|
||||
return (0 < value) ? value : (-value);
|
||||
}
|
||||
};
|
||||
constexpr auto abs = abs_helper{};
|
||||
|
||||
constexpr auto add = [](auto value) {
|
||||
return [value](auto other) {
|
||||
return value + other;
|
||||
};
|
||||
};
|
||||
|
||||
struct negate_helper {
|
||||
template <
|
||||
typename Type,
|
||||
typename = decltype(-std::declval<Type>())>
|
||||
constexpr Type operator()(Type value) const {
|
||||
return -value;
|
||||
}
|
||||
};
|
||||
constexpr auto negate = negate_helper{};
|
||||
|
||||
} // namespace functors
|
||||
} // namespace base
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/assertion.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Container>
|
||||
class index_based_iterator {
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
using value_type = typename Container::value_type;
|
||||
using difference_type = typename Container::difference_type;
|
||||
using pointer = std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
typename Container::const_pointer,
|
||||
typename Container::pointer>;
|
||||
using reference = std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
typename Container::const_reference,
|
||||
typename Container::reference>;
|
||||
using base_type = std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
typename Container::const_iterator,
|
||||
typename Container::iterator>;
|
||||
|
||||
index_based_iterator(Container *container, base_type impl)
|
||||
: _container(container)
|
||||
, _index(impl - _container->begin()) {
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
return *(_container->begin() + _index);
|
||||
}
|
||||
pointer operator->() const {
|
||||
return std::addressof(**this);
|
||||
}
|
||||
index_based_iterator &operator++() {
|
||||
++_index;
|
||||
return *this;
|
||||
}
|
||||
index_based_iterator operator++(int) {
|
||||
auto copy = *this;
|
||||
++*this;
|
||||
return copy;
|
||||
}
|
||||
index_based_iterator &operator--() {
|
||||
--_index;
|
||||
return *this;
|
||||
}
|
||||
index_based_iterator operator--(int) {
|
||||
auto copy = *this;
|
||||
--*this;
|
||||
return copy;
|
||||
}
|
||||
index_based_iterator &operator+=(difference_type offset) {
|
||||
_index += offset;
|
||||
return *this;
|
||||
}
|
||||
index_based_iterator operator+(difference_type offset) const {
|
||||
auto copy = *this;
|
||||
copy += offset;
|
||||
return copy;
|
||||
}
|
||||
index_based_iterator &operator-=(difference_type offset) {
|
||||
_index -= offset;
|
||||
return *this;
|
||||
}
|
||||
index_based_iterator operator-(difference_type offset) const {
|
||||
auto copy = *this;
|
||||
copy -= offset;
|
||||
return copy;
|
||||
}
|
||||
difference_type operator-(const index_based_iterator &other) const {
|
||||
return _index - other._index;
|
||||
}
|
||||
reference operator[](difference_type offset) const {
|
||||
return *(*this + offset);
|
||||
}
|
||||
|
||||
bool operator==(const index_based_iterator &other) const {
|
||||
Expects(_container == other._container);
|
||||
return _index == other._index;
|
||||
}
|
||||
bool operator!=(const index_based_iterator &other) const {
|
||||
Expects(_container == other._container);
|
||||
return _index != other._index;
|
||||
}
|
||||
bool operator<(const index_based_iterator &other) const {
|
||||
Expects(_container == other._container);
|
||||
return _index < other._index;
|
||||
}
|
||||
bool operator>(const index_based_iterator &other) const {
|
||||
return other < *this;
|
||||
}
|
||||
bool operator<=(const index_based_iterator &other) const {
|
||||
return !(other < *this);
|
||||
}
|
||||
bool operator>=(const index_based_iterator &other) const {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
base_type base() const {
|
||||
return _container->begin() + _index;
|
||||
}
|
||||
|
||||
private:
|
||||
Container *_container = nullptr;
|
||||
difference_type _index = 0;
|
||||
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
index_based_iterator<Container> index_based_begin(Container &container) {
|
||||
return { &container, std::begin(container) };
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
index_based_iterator<Container> index_based_end(Container &container) {
|
||||
return { &container, std::end(container) };
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QEvent>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include "base/basic_types.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class InvokeQueuedEvent : public QEvent {
|
||||
public:
|
||||
static constexpr auto kType = QEvent::Type(60666);
|
||||
|
||||
explicit InvokeQueuedEvent(FnMut<void()> &&method)
|
||||
: QEvent(kType)
|
||||
, _method(std::move(method)) {
|
||||
}
|
||||
|
||||
void invoke() {
|
||||
_method();
|
||||
}
|
||||
|
||||
private:
|
||||
FnMut<void()> _method;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
template <typename Lambda>
|
||||
inline void InvokeQueued(const QObject *context, Lambda &&lambda) {
|
||||
QCoreApplication::postEvent(
|
||||
const_cast<QObject*>(context),
|
||||
new base::InvokeQueuedEvent(std::forward<Lambda>(lambda)));
|
||||
}
|
||||
|
||||
class SingleQueuedInvokation : public QObject {
|
||||
public:
|
||||
SingleQueuedInvokation(Fn<void()> callback) : _callback(callback) {
|
||||
}
|
||||
void call() {
|
||||
if (_pending.testAndSetAcquire(0, 1)) {
|
||||
InvokeQueued(this, [this] {
|
||||
if (_pending.testAndSetRelease(1, 0)) {
|
||||
_callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Fn<void()> _callback;
|
||||
QAtomicInt _pending = { 0 };
|
||||
|
||||
};
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Entry>
|
||||
class last_used_cache {
|
||||
public:
|
||||
void up(Entry entry);
|
||||
void remove(Entry entry);
|
||||
void clear();
|
||||
|
||||
Entry take_lowest();
|
||||
|
||||
private:
|
||||
std::list<Entry> _queue;
|
||||
std::unordered_map<Entry, typename std::list<Entry>::iterator> _map;
|
||||
|
||||
};
|
||||
|
||||
template <typename Entry>
|
||||
void last_used_cache<Entry>::up(Entry entry) {
|
||||
if (!_queue.empty() && _queue.back() == entry) {
|
||||
return;
|
||||
}
|
||||
const auto i = _map.find(entry);
|
||||
if (i != end(_map)) {
|
||||
_queue.splice(end(_queue), _queue, i->second);
|
||||
} else {
|
||||
_map.emplace(entry, _queue.insert(end(_queue), entry));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Entry>
|
||||
void last_used_cache<Entry>::remove(Entry entry) {
|
||||
const auto i = _map.find(entry);
|
||||
if (i != end(_map)) {
|
||||
_queue.erase(i->second);
|
||||
_map.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Entry>
|
||||
void last_used_cache<Entry>::clear() {
|
||||
_queue.clear();
|
||||
_map.clear();
|
||||
}
|
||||
|
||||
template <typename Entry>
|
||||
Entry last_used_cache<Entry>::take_lowest() {
|
||||
if (_queue.empty()) {
|
||||
return Entry();
|
||||
}
|
||||
auto result = std::move(_queue.front());
|
||||
_queue.erase(begin(_queue));
|
||||
_map.erase(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/details/callable.h>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Data, typename Method, typename ...Methods>
|
||||
inline decltype(auto) match_method(
|
||||
Data &&data,
|
||||
Method &&method,
|
||||
Methods &&...methods) {
|
||||
using namespace rpl::details;
|
||||
if constexpr (is_callable_plain_v<Method, Data&&>) {
|
||||
return std::forward<Method>(method)(std::forward<Data>(data));
|
||||
} else {
|
||||
return match_method(
|
||||
std::forward<Data>(data),
|
||||
std::forward<Methods>(methods)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Data1,
|
||||
typename Data2,
|
||||
typename Method,
|
||||
typename ...Methods>
|
||||
inline decltype(auto) match_method2(
|
||||
Data1 &&data1,
|
||||
Data2 &&data2,
|
||||
Method &&method,
|
||||
Methods &&...methods) {
|
||||
using namespace rpl::details;
|
||||
if constexpr (is_callable_plain_v<Method, Data1&&, Data2&&>) {
|
||||
return std::forward<Method>(method)(
|
||||
std::forward<Data1>(data1),
|
||||
std::forward<Data2>(data2));
|
||||
} else {
|
||||
return match_method2(
|
||||
std::forward<Data1>(data1),
|
||||
std::forward<Data2>(data2),
|
||||
std::forward<Methods>(methods)...);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
// Smart pointer for QObject*, has move semantics, destroys object if it doesn't have a parent.
|
||||
template <typename Object>
|
||||
class object_ptr {
|
||||
public:
|
||||
object_ptr(std::nullptr_t) noexcept {
|
||||
}
|
||||
|
||||
// No default constructor, but constructors with at least
|
||||
// one argument are simply make functions.
|
||||
template <typename Parent, typename... Args>
|
||||
explicit object_ptr(Parent &&parent, Args&&... args)
|
||||
: _object(new Object(std::forward<Parent>(parent), std::forward<Args>(args)...)) {
|
||||
}
|
||||
static object_ptr<Object> fromRaw(Object *value) noexcept {
|
||||
object_ptr<Object> result = { nullptr };
|
||||
result._object = value;
|
||||
return result;
|
||||
}
|
||||
Object *release() noexcept {
|
||||
return static_cast<Object*>(base::take(_object).data());
|
||||
}
|
||||
|
||||
object_ptr(const object_ptr &other) = delete;
|
||||
object_ptr &operator=(const object_ptr &other) = delete;
|
||||
object_ptr(object_ptr &&other) noexcept : _object(base::take(other._object)) {
|
||||
}
|
||||
object_ptr &operator=(object_ptr &&other) noexcept {
|
||||
auto temp = std::move(other);
|
||||
destroy();
|
||||
std::swap(_object, temp._object);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherObject,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<Object, OtherObject>>>
|
||||
object_ptr(object_ptr<OtherObject> &&other) noexcept
|
||||
: _object(base::take(other._object)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherObject,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<Object, OtherObject>>>
|
||||
object_ptr &operator=(object_ptr<OtherObject> &&other) noexcept {
|
||||
_object = base::take(other._object);
|
||||
return *this;
|
||||
}
|
||||
|
||||
object_ptr &operator=(std::nullptr_t) noexcept {
|
||||
_object = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// So we can pass this pointer to methods like connect().
|
||||
Object *data() const noexcept {
|
||||
return static_cast<Object*>(_object.data());
|
||||
}
|
||||
operator Object*() const noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return _object != nullptr;
|
||||
}
|
||||
|
||||
Object *operator->() const noexcept {
|
||||
return data();
|
||||
}
|
||||
Object &operator*() const noexcept {
|
||||
return *data();
|
||||
}
|
||||
|
||||
// Use that instead "= new Object(parent, ...)"
|
||||
template <typename Parent, typename... Args>
|
||||
Object *create(Parent &&parent, Args&&... args) {
|
||||
destroy();
|
||||
_object = new Object(
|
||||
std::forward<Parent>(parent),
|
||||
std::forward<Args>(args)...);
|
||||
return data();
|
||||
}
|
||||
void destroy() noexcept {
|
||||
delete base::take(_object);
|
||||
}
|
||||
void destroyDelayed() {
|
||||
if (_object) {
|
||||
if (auto widget = base::up_cast<QWidget*>(data())) {
|
||||
widget->hide();
|
||||
}
|
||||
base::take(_object)->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
~object_ptr() noexcept {
|
||||
if (auto pointer = _object) {
|
||||
if (!pointer->parent()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename OtherObject>
|
||||
friend class object_ptr;
|
||||
|
||||
QPointer<QObject> _object;
|
||||
|
||||
};
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/observer.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
namespace {
|
||||
|
||||
bool CantUseObservables = false;
|
||||
void (*HandleDelayedMethod)() = nullptr;
|
||||
|
||||
struct ObservableListWrap {
|
||||
~ObservableListWrap() {
|
||||
CantUseObservables = true;
|
||||
}
|
||||
OrderedSet<ObservableCallHandlers*> list;
|
||||
};
|
||||
|
||||
ObservableListWrap &PendingObservables() {
|
||||
static ObservableListWrap result;
|
||||
return result;
|
||||
}
|
||||
|
||||
ObservableListWrap &ActiveObservables() {
|
||||
static ObservableListWrap result;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void RegisterPendingObservable(ObservableCallHandlers *handlers) {
|
||||
if (CantUseObservables) return;
|
||||
PendingObservables().list.insert(handlers);
|
||||
if (HandleDelayedMethod) {
|
||||
HandleDelayedMethod();
|
||||
}
|
||||
}
|
||||
|
||||
void UnregisterActiveObservable(ObservableCallHandlers *handlers) {
|
||||
if (CantUseObservables) return;
|
||||
ActiveObservables().list.remove(handlers);
|
||||
}
|
||||
|
||||
void UnregisterObservable(ObservableCallHandlers *handlers) {
|
||||
if (CantUseObservables) return;
|
||||
PendingObservables().list.remove(handlers);
|
||||
ActiveObservables().list.remove(handlers);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void InitObservables(void(*HandleDelayed)()) {
|
||||
internal::HandleDelayedMethod = HandleDelayed;
|
||||
}
|
||||
|
||||
void HandleObservables() {
|
||||
if (internal::CantUseObservables) return;
|
||||
auto &active = internal::ActiveObservables().list;
|
||||
qSwap(active, internal::PendingObservables().list);
|
||||
while (!active.empty()) {
|
||||
auto first = *active.begin();
|
||||
(*first)();
|
||||
if (!active.empty() && *active.begin() == first) {
|
||||
active.erase(active.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<> ObservableViewer(base::Observable<void> &observable) {
|
||||
return [&observable](const auto &consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
lifetime.make_state<base::Subscription>(
|
||||
observable.add_subscription([consumer]() {
|
||||
consumer.put_next({});
|
||||
}));
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,476 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <rpl/producer.h>
|
||||
#include "base/type_traits.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
using ObservableCallHandlers = Fn<void()>;
|
||||
void RegisterPendingObservable(ObservableCallHandlers *handlers);
|
||||
void UnregisterActiveObservable(ObservableCallHandlers *handlers);
|
||||
void UnregisterObservable(ObservableCallHandlers *handlers);
|
||||
|
||||
template <typename EventType>
|
||||
struct SubscriptionHandlerHelper {
|
||||
using type = Fn<void(parameter_type<EventType>)>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SubscriptionHandlerHelper<void> {
|
||||
using type = Fn<void()>;
|
||||
};
|
||||
|
||||
template <typename EventType>
|
||||
using SubscriptionHandler = typename SubscriptionHandlerHelper<EventType>::type;
|
||||
|
||||
class BaseObservableData {
|
||||
};
|
||||
|
||||
template <typename EventType, typename Handler>
|
||||
class CommonObservableData;
|
||||
|
||||
template <typename EventType, typename Handler>
|
||||
class ObservableData;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
class Subscription {
|
||||
public:
|
||||
Subscription() = default;
|
||||
Subscription(const Subscription &) = delete;
|
||||
Subscription &operator=(const Subscription &) = delete;
|
||||
Subscription(Subscription &&other) : _node(base::take(other._node)), _removeAndDestroyMethod(other._removeAndDestroyMethod) {
|
||||
}
|
||||
Subscription &operator=(Subscription &&other) {
|
||||
qSwap(_node, other._node);
|
||||
qSwap(_removeAndDestroyMethod, other._removeAndDestroyMethod);
|
||||
return *this;
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return (_node != nullptr);
|
||||
}
|
||||
void destroy() {
|
||||
if (_node) {
|
||||
(*_removeAndDestroyMethod)(base::take(_node));
|
||||
}
|
||||
}
|
||||
~Subscription() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
Node(const std::shared_ptr<internal::BaseObservableData> &observable)
|
||||
: observable(observable) {
|
||||
}
|
||||
Node *next = nullptr;
|
||||
Node *prev = nullptr;
|
||||
std::weak_ptr<internal::BaseObservableData> observable;
|
||||
};
|
||||
using RemoveAndDestroyMethod = void(*)(Node*);
|
||||
Subscription(Node *node, RemoveAndDestroyMethod removeAndDestroyMethod)
|
||||
: _node(node)
|
||||
, _removeAndDestroyMethod(removeAndDestroyMethod) {
|
||||
}
|
||||
|
||||
Node *_node = nullptr;
|
||||
RemoveAndDestroyMethod _removeAndDestroyMethod;
|
||||
|
||||
template <typename EventType, typename Handler>
|
||||
friend class internal::CommonObservableData;
|
||||
|
||||
template <typename EventType, typename Handler>
|
||||
friend class internal::ObservableData;
|
||||
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename EventType, typename Handler, bool EventTypeIsSimple>
|
||||
class BaseObservable;
|
||||
|
||||
template <typename EventType, typename Handler>
|
||||
class CommonObservable {
|
||||
public:
|
||||
Subscription add_subscription(Handler &&handler) {
|
||||
if (!_data) {
|
||||
_data = std::make_shared<ObservableData<EventType, Handler>>(this);
|
||||
}
|
||||
return _data->append(std::move(handler));
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ObservableData<EventType, Handler>> _data;
|
||||
|
||||
friend class CommonObservableData<EventType, Handler>;
|
||||
friend class BaseObservable<EventType, Handler, base::type_traits<EventType>::is_fast_copy_type::value>;
|
||||
|
||||
};
|
||||
|
||||
template <typename EventType, typename Handler>
|
||||
class BaseObservable<EventType, Handler, true> : public internal::CommonObservable<EventType, Handler> {
|
||||
public:
|
||||
void notify(EventType event, bool sync = false) {
|
||||
if (this->_data) {
|
||||
this->_data->notify(std::move(event), sync);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename EventType, typename Handler>
|
||||
class BaseObservable<EventType, Handler, false> : public internal::CommonObservable<EventType, Handler> {
|
||||
public:
|
||||
void notify(EventType &&event, bool sync = false) {
|
||||
if (this->_data) {
|
||||
this->_data->notify(std::move(event), sync);
|
||||
}
|
||||
}
|
||||
void notify(const EventType &event, bool sync = false) {
|
||||
if (this->_data) {
|
||||
auto event_copy = event;
|
||||
this->_data->notify(std::move(event_copy), sync);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename EventType, typename Handler>
|
||||
class CommonObservableData : public BaseObservableData {
|
||||
public:
|
||||
CommonObservableData(CommonObservable<EventType, Handler> *observable) : _observable(observable) {
|
||||
}
|
||||
|
||||
Subscription append(Handler &&handler) {
|
||||
auto node = new Node(_observable->_data, std::move(handler));
|
||||
if (_begin) {
|
||||
_end->next = node;
|
||||
node->prev = _end;
|
||||
_end = node;
|
||||
} else {
|
||||
_begin = _end = node;
|
||||
}
|
||||
return { _end, &CommonObservableData::removeAndDestroyNode };
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return !_begin;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node : public Subscription::Node {
|
||||
Node(
|
||||
const std::shared_ptr<BaseObservableData> &observer,
|
||||
Handler &&handler)
|
||||
: Subscription::Node(observer)
|
||||
, handler(std::move(handler)) {
|
||||
}
|
||||
Handler handler;
|
||||
};
|
||||
|
||||
void remove(Subscription::Node *node) {
|
||||
if (node->prev) {
|
||||
node->prev->next = node->next;
|
||||
}
|
||||
if (node->next) {
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
if (_begin == node) {
|
||||
_begin = static_cast<Node*>(node->next);
|
||||
}
|
||||
if (_end == node) {
|
||||
_end = static_cast<Node*>(node->prev);
|
||||
}
|
||||
if (_current == node) {
|
||||
_current = static_cast<Node*>(node->prev);
|
||||
} else if (!_begin) {
|
||||
_observable->_data.reset();
|
||||
}
|
||||
}
|
||||
|
||||
static void removeAndDestroyNode(Subscription::Node *node) {
|
||||
if (const auto that = node->observable.lock()) {
|
||||
static_cast<CommonObservableData*>(that.get())->remove(node);
|
||||
}
|
||||
delete static_cast<Node*>(node);
|
||||
}
|
||||
|
||||
template <typename CallCurrent>
|
||||
void notifyEnumerate(CallCurrent callCurrent) {
|
||||
_current = _begin;
|
||||
do {
|
||||
callCurrent();
|
||||
if (_current) {
|
||||
_current = static_cast<Node*>(_current->next);
|
||||
} else if (_begin) {
|
||||
_current = _begin;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (_current);
|
||||
}
|
||||
|
||||
bool destroyMeIfEmpty() const {
|
||||
if (empty()) {
|
||||
_observable->_data.reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CommonObservable<EventType, Handler> *_observable = nullptr;
|
||||
Node *_begin = nullptr;
|
||||
Node *_current = nullptr;
|
||||
Node *_end = nullptr;
|
||||
ObservableCallHandlers _callHandlers;
|
||||
|
||||
friend class ObservableData<EventType, Handler>;
|
||||
|
||||
};
|
||||
|
||||
template <typename EventType, typename Handler>
|
||||
class ObservableData : public CommonObservableData<EventType, Handler> {
|
||||
public:
|
||||
using CommonObservableData<EventType, Handler>::CommonObservableData;
|
||||
|
||||
void notify(EventType &&event, bool sync) {
|
||||
if (_handling) {
|
||||
sync = false;
|
||||
}
|
||||
if (sync) {
|
||||
_events.push_back(std::move(event));
|
||||
callHandlers();
|
||||
} else {
|
||||
if (!this->_callHandlers) {
|
||||
this->_callHandlers = [this]() {
|
||||
callHandlers();
|
||||
};
|
||||
}
|
||||
if (_events.empty()) {
|
||||
RegisterPendingObservable(&this->_callHandlers);
|
||||
}
|
||||
_events.push_back(std::move(event));
|
||||
}
|
||||
}
|
||||
|
||||
~ObservableData() {
|
||||
UnregisterObservable(&this->_callHandlers);
|
||||
}
|
||||
|
||||
private:
|
||||
void callHandlers() {
|
||||
_handling = true;
|
||||
auto events = base::take(_events);
|
||||
for (auto &event : events) {
|
||||
this->notifyEnumerate([this, &event]() {
|
||||
this->_current->handler(event);
|
||||
});
|
||||
if (this->destroyMeIfEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_handling = false;
|
||||
UnregisterActiveObservable(&this->_callHandlers);
|
||||
}
|
||||
|
||||
std::deque<EventType> _events;
|
||||
bool _handling = false;
|
||||
|
||||
};
|
||||
|
||||
template <class Handler>
|
||||
class ObservableData<void, Handler> : public CommonObservableData<void, Handler> {
|
||||
public:
|
||||
using CommonObservableData<void, Handler>::CommonObservableData;
|
||||
|
||||
void notify(bool sync) {
|
||||
if (_handling) {
|
||||
sync = false;
|
||||
}
|
||||
if (sync) {
|
||||
++_eventsCount;
|
||||
callHandlers();
|
||||
} else {
|
||||
if (!this->_callHandlers) {
|
||||
this->_callHandlers = [this]() {
|
||||
callHandlers();
|
||||
};
|
||||
}
|
||||
if (!_eventsCount) {
|
||||
RegisterPendingObservable(&this->_callHandlers);
|
||||
}
|
||||
++_eventsCount;
|
||||
}
|
||||
}
|
||||
|
||||
~ObservableData() {
|
||||
UnregisterObservable(&this->_callHandlers);
|
||||
}
|
||||
|
||||
private:
|
||||
void callHandlers() {
|
||||
_handling = true;
|
||||
auto eventsCount = base::take(_eventsCount);
|
||||
for (int i = 0; i != eventsCount; ++i) {
|
||||
this->notifyEnumerate([this]() {
|
||||
this->_current->handler();
|
||||
});
|
||||
if (this->destroyMeIfEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_handling = false;
|
||||
UnregisterActiveObservable(&this->_callHandlers);
|
||||
}
|
||||
|
||||
int _eventsCount = 0;
|
||||
bool _handling = false;
|
||||
|
||||
};
|
||||
|
||||
template <typename Handler>
|
||||
class BaseObservable<void, Handler, base::type_traits<void>::is_fast_copy_type::value> : public internal::CommonObservable<void, Handler> {
|
||||
public:
|
||||
void notify(bool sync = false) {
|
||||
if (this->_data) {
|
||||
this->_data->notify(sync);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename EventType, typename Handler = internal::SubscriptionHandler<EventType>>
|
||||
class Observable : public internal::BaseObservable<EventType, Handler, base::type_traits<EventType>::is_fast_copy_type::value> {
|
||||
public:
|
||||
Observable() = default;
|
||||
Observable(const Observable &other) = delete;
|
||||
Observable(Observable &&other) = default;
|
||||
Observable &operator=(const Observable &other) = delete;
|
||||
Observable &operator=(Observable &&other) = default;
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class Variable {
|
||||
public:
|
||||
Variable(parameter_type<Type> startValue = Type()) : _value(startValue) {
|
||||
}
|
||||
Variable(Variable &&other) = default;
|
||||
Variable &operator=(Variable &&other) = default;
|
||||
|
||||
parameter_type<Type> value() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
void setForced(parameter_type<Type> newValue, bool sync = false) {
|
||||
_value = newValue;
|
||||
changed().notify(_value, sync);
|
||||
}
|
||||
|
||||
void set(parameter_type<Type> newValue, bool sync = false) {
|
||||
if (_value != newValue) {
|
||||
setForced(newValue, sync);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
void process(Callback callback, bool sync = false) {
|
||||
callback(_value);
|
||||
changed().notify(_value, sync);
|
||||
}
|
||||
|
||||
Observable<Type> &changed() const {
|
||||
return _changed;
|
||||
}
|
||||
|
||||
private:
|
||||
Type _value;
|
||||
mutable Observable<Type> _changed;
|
||||
|
||||
};
|
||||
|
||||
class Subscriber {
|
||||
protected:
|
||||
template <typename EventType, typename Handler, typename Lambda>
|
||||
int subscribe(base::Observable<EventType, Handler> &observable, Lambda &&handler) {
|
||||
_subscriptions.push_back(observable.add_subscription(std::forward<Lambda>(handler)));
|
||||
return _subscriptions.size();
|
||||
}
|
||||
|
||||
template <typename EventType, typename Handler, typename Lambda>
|
||||
int subscribe(base::Observable<EventType, Handler> *observable, Lambda &&handler) {
|
||||
return subscribe(*observable, std::forward<Lambda>(handler));
|
||||
}
|
||||
|
||||
template <typename Type, typename Lambda>
|
||||
int subscribe(const base::Variable<Type> &variable, Lambda &&handler) {
|
||||
return subscribe(variable.changed(), std::forward<Lambda>(handler));
|
||||
}
|
||||
|
||||
template <typename Type, typename Lambda>
|
||||
int subscribe(const base::Variable<Type> *variable, Lambda &&handler) {
|
||||
return subscribe(variable->changed(), std::forward<Lambda>(handler));
|
||||
}
|
||||
|
||||
void unsubscribe(int index) {
|
||||
if (!index) return;
|
||||
auto count = static_cast<int>(_subscriptions.size());
|
||||
Assert(index > 0 && index <= count);
|
||||
_subscriptions[index - 1].destroy();
|
||||
if (index == count) {
|
||||
while (index > 0 && !_subscriptions[--index]) {
|
||||
_subscriptions.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Subscriber() {
|
||||
auto subscriptions = base::take(_subscriptions);
|
||||
for (auto &subscription : subscriptions) {
|
||||
subscription.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<base::Subscription> _subscriptions;
|
||||
|
||||
};
|
||||
|
||||
void InitObservables(void(*HandleDelayed)());
|
||||
void HandleObservables();
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
typename = std::enable_if_t<!std::is_same_v<Type, void>>>
|
||||
inline auto ObservableViewer(base::Observable<Type> &observable) {
|
||||
return rpl::make_producer<Type>([&observable](
|
||||
const auto &consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
lifetime.make_state<base::Subscription>(
|
||||
observable.add_subscription([consumer](auto &&update) {
|
||||
consumer.put_next_forward(
|
||||
std::forward<decltype(update)>(update));
|
||||
}));
|
||||
return lifetime;
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<> ObservableViewer(base::Observable<void> &observable);
|
||||
|
||||
} // namespace base
|
|
@ -1,577 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/bytes.h"
|
||||
#include "base/algorithm.h"
|
||||
#include "base/basic_types.h"
|
||||
|
||||
extern "C" {
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/modes.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
} // extern "C"
|
||||
|
||||
#ifdef small
|
||||
#undef small
|
||||
#endif // small
|
||||
|
||||
namespace openssl {
|
||||
|
||||
class Context {
|
||||
public:
|
||||
Context() : _data(BN_CTX_new()) {
|
||||
}
|
||||
Context(const Context &other) = delete;
|
||||
Context(Context &&other) : _data(base::take(other._data)) {
|
||||
}
|
||||
Context &operator=(const Context &other) = delete;
|
||||
Context &operator=(Context &&other) {
|
||||
_data = base::take(other._data);
|
||||
return *this;
|
||||
}
|
||||
~Context() {
|
||||
if (_data) {
|
||||
BN_CTX_free(_data);
|
||||
}
|
||||
}
|
||||
|
||||
BN_CTX *raw() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
private:
|
||||
BN_CTX *_data = nullptr;
|
||||
|
||||
};
|
||||
|
||||
class BigNum {
|
||||
public:
|
||||
BigNum() = default;
|
||||
BigNum(const BigNum &other)
|
||||
: _data((other.failed() || other.isZero())
|
||||
? nullptr
|
||||
: BN_dup(other.raw()))
|
||||
, _failed(other._failed) {
|
||||
}
|
||||
BigNum(BigNum &&other)
|
||||
: _data(std::exchange(other._data, nullptr))
|
||||
, _failed(std::exchange(other._failed, false)) {
|
||||
}
|
||||
BigNum &operator=(const BigNum &other) {
|
||||
if (other.failed()) {
|
||||
_failed = true;
|
||||
} else if (other.isZero()) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else if (!_data) {
|
||||
_data = BN_dup(other.raw());
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_copy(raw(), other.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &operator=(BigNum &&other) {
|
||||
std::swap(_data, other._data);
|
||||
std::swap(_failed, other._failed);
|
||||
return *this;
|
||||
}
|
||||
~BigNum() {
|
||||
clear();
|
||||
}
|
||||
|
||||
explicit BigNum(unsigned int word) : BigNum() {
|
||||
setWord(word);
|
||||
}
|
||||
explicit BigNum(bytes::const_span bytes) : BigNum() {
|
||||
setBytes(bytes);
|
||||
}
|
||||
|
||||
BigNum &setWord(unsigned int word) {
|
||||
if (!word) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_set_word(raw(), word);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setBytes(bytes::const_span bytes) {
|
||||
if (bytes.empty()) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_bin2bn(
|
||||
reinterpret_cast<const unsigned char*>(bytes.data()),
|
||||
bytes.size(),
|
||||
raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNum &setAdd(const BigNum &a, const BigNum &b) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_add(raw(), a.raw(), b.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setSub(const BigNum &a, const BigNum &b) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_sub(raw(), a.raw(), b.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_mul(raw(), a.raw(), b.raw(), context.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModAdd(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_add(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModSub(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_sub(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_mul(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModInverse(
|
||||
const BigNum &a,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_inverse(raw(), a.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModExp(
|
||||
const BigNum &base,
|
||||
const BigNum &power,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (base.failed() || power.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (base.isNegative() || power.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_exp(raw(), base.raw(), power.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isZero() const {
|
||||
return !failed() && (!_data || BN_is_zero(raw()));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isOne() const {
|
||||
return !failed() && _data && BN_is_one(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isNegative() const {
|
||||
return !failed() && _data && BN_is_negative(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isPrime(const Context &context = Context()) const {
|
||||
if (failed() || !_data) {
|
||||
return false;
|
||||
}
|
||||
constexpr auto kMillerRabinIterationCount = 30;
|
||||
const auto result = BN_is_prime_ex(
|
||||
raw(),
|
||||
kMillerRabinIterationCount,
|
||||
context.raw(),
|
||||
nullptr);
|
||||
if (result == 1) {
|
||||
return true;
|
||||
} else if (result != 0) {
|
||||
_failed = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BigNum &subWord(unsigned int word) {
|
||||
if (failed()) {
|
||||
return *this;
|
||||
} else if (!BN_sub_word(raw(), word)) {
|
||||
_failed = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &divWord(BN_ULONG word, BN_ULONG *mod = nullptr) {
|
||||
Expects(word != 0);
|
||||
|
||||
const auto result = failed()
|
||||
? (BN_ULONG)-1
|
||||
: BN_div_word(raw(), word);
|
||||
if (result == (BN_ULONG)-1) {
|
||||
_failed = true;
|
||||
}
|
||||
if (mod) {
|
||||
*mod = result;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
[[nodiscard]] BN_ULONG countModWord(BN_ULONG word) const {
|
||||
Expects(word != 0);
|
||||
|
||||
return failed() ? (BN_ULONG)-1 : BN_mod_word(raw(), word);
|
||||
}
|
||||
|
||||
[[nodiscard]] int bitsSize() const {
|
||||
return failed() ? 0 : BN_num_bits(raw());
|
||||
}
|
||||
[[nodiscard]] int bytesSize() const {
|
||||
return failed() ? 0 : BN_num_bytes(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bytes::vector getBytes() const {
|
||||
if (failed()) {
|
||||
return {};
|
||||
}
|
||||
auto length = BN_num_bytes(raw());
|
||||
auto result = bytes::vector(length);
|
||||
auto resultSize = BN_bn2bin(
|
||||
raw(),
|
||||
reinterpret_cast<unsigned char*>(result.data()));
|
||||
Assert(resultSize == length);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] BIGNUM *raw() {
|
||||
if (!_data) _data = BN_new();
|
||||
return _data;
|
||||
}
|
||||
[[nodiscard]] const BIGNUM *raw() const {
|
||||
if (!_data) _data = BN_new();
|
||||
return _data;
|
||||
}
|
||||
[[nodiscard]] BIGNUM *takeRaw() {
|
||||
return _failed
|
||||
? nullptr
|
||||
: _data
|
||||
? std::exchange(_data, nullptr)
|
||||
: BN_new();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool failed() const {
|
||||
return _failed;
|
||||
}
|
||||
|
||||
[[nodiscard]] static BigNum Add(const BigNum &a, const BigNum &b) {
|
||||
return BigNum().setAdd(a, b);
|
||||
}
|
||||
[[nodiscard]] static BigNum Sub(const BigNum &a, const BigNum &b) {
|
||||
return BigNum().setSub(a, b);
|
||||
}
|
||||
[[nodiscard]] static BigNum Mul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setMul(a, b, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModAdd(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModAdd(a, b, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModSub(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModSub(a, b, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModMul(a, b, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModInverse(
|
||||
const BigNum &a,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModInverse(a, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModExp(
|
||||
const BigNum &base,
|
||||
const BigNum &power,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModExp(base, power, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum Failed() {
|
||||
auto result = BigNum();
|
||||
result._failed = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void clear() {
|
||||
BN_clear_free(std::exchange(_data, nullptr));
|
||||
}
|
||||
|
||||
mutable BIGNUM *_data = nullptr;
|
||||
mutable bool _failed = false;
|
||||
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Context, typename Method, typename Arg>
|
||||
inline void ShaUpdate(Context context, Method method, Arg &&arg) {
|
||||
const auto span = bytes::make_span(arg);
|
||||
method(context, span.data(), span.size());
|
||||
}
|
||||
|
||||
template <typename Context, typename Method, typename Arg, typename ...Args>
|
||||
inline void ShaUpdate(Context context, Method method, Arg &&arg, Args &&...args) {
|
||||
const auto span = bytes::make_span(arg);
|
||||
method(context, span.data(), span.size());
|
||||
ShaUpdate(context, method, args...);
|
||||
}
|
||||
|
||||
template <size_type Size, typename Method>
|
||||
inline bytes::vector Sha(Method method, bytes::const_span data) {
|
||||
auto result = bytes::vector(Size);
|
||||
method(
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
data.size(),
|
||||
reinterpret_cast<unsigned char*>(result.data()));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <
|
||||
size_type Size,
|
||||
typename Context,
|
||||
typename Init,
|
||||
typename Update,
|
||||
typename Finalize,
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
bytes::vector Sha(
|
||||
Context context,
|
||||
Init init,
|
||||
Update update,
|
||||
Finalize finalize,
|
||||
Args &&...args) {
|
||||
auto result = bytes::vector(Size);
|
||||
|
||||
init(&context);
|
||||
ShaUpdate(&context, update, args...);
|
||||
finalize(reinterpret_cast<unsigned char*>(result.data()), &context);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <
|
||||
size_type Size,
|
||||
typename Evp>
|
||||
bytes::vector Pbkdf2(
|
||||
bytes::const_span password,
|
||||
bytes::const_span salt,
|
||||
int iterations,
|
||||
Evp evp) {
|
||||
auto result = bytes::vector(Size);
|
||||
PKCS5_PBKDF2_HMAC(
|
||||
reinterpret_cast<const char*>(password.data()),
|
||||
password.size(),
|
||||
reinterpret_cast<const unsigned char*>(salt.data()),
|
||||
salt.size(),
|
||||
iterations,
|
||||
evp,
|
||||
result.size(),
|
||||
reinterpret_cast<unsigned char*>(result.data()));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
constexpr auto kSha1Size = size_type(SHA_DIGEST_LENGTH);
|
||||
constexpr auto kSha256Size = size_type(SHA256_DIGEST_LENGTH);
|
||||
constexpr auto kSha512Size = size_type(SHA512_DIGEST_LENGTH);
|
||||
|
||||
inline bytes::vector Sha1(bytes::const_span data) {
|
||||
return details::Sha<kSha1Size>(SHA1, data);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
inline bytes::vector Sha1(Args &&...args) {
|
||||
return details::Sha<kSha1Size>(
|
||||
SHA_CTX(),
|
||||
SHA1_Init,
|
||||
SHA1_Update,
|
||||
SHA1_Final,
|
||||
args...);
|
||||
}
|
||||
|
||||
inline bytes::vector Sha256(bytes::const_span data) {
|
||||
return details::Sha<kSha256Size>(SHA256, data);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
inline bytes::vector Sha256(Args &&...args) {
|
||||
return details::Sha<kSha256Size>(
|
||||
SHA256_CTX(),
|
||||
SHA256_Init,
|
||||
SHA256_Update,
|
||||
SHA256_Final,
|
||||
args...);
|
||||
}
|
||||
|
||||
inline bytes::vector Sha512(bytes::const_span data) {
|
||||
return details::Sha<kSha512Size>(SHA512, data);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
inline bytes::vector Sha512(Args &&...args) {
|
||||
return details::Sha<kSha512Size>(
|
||||
SHA512_CTX(),
|
||||
SHA512_Init,
|
||||
SHA512_Update,
|
||||
SHA512_Final,
|
||||
args...);
|
||||
}
|
||||
|
||||
inline void AddRandomSeed(bytes::const_span data) {
|
||||
RAND_seed(data.data(), data.size());
|
||||
}
|
||||
|
||||
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||
[[nodiscard]] inline T RandomValue() {
|
||||
unsigned char buffer[sizeof(T)];
|
||||
if (!RAND_bytes(buffer, sizeof(T))) {
|
||||
Unexpected("Could not generate random bytes!");
|
||||
}
|
||||
auto result = T();
|
||||
memcpy(&result, buffer, sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bytes::vector Pbkdf2Sha512(
|
||||
bytes::const_span password,
|
||||
bytes::const_span salt,
|
||||
int iterations) {
|
||||
return details::Pbkdf2<kSha512Size>(
|
||||
password,
|
||||
salt,
|
||||
iterations,
|
||||
EVP_sha512());
|
||||
}
|
||||
|
||||
inline bytes::vector HmacSha256(
|
||||
bytes::const_span key,
|
||||
bytes::const_span data) {
|
||||
auto result = bytes::vector(kSha256Size);
|
||||
auto length = (unsigned int)kSha256Size;
|
||||
|
||||
HMAC(
|
||||
EVP_sha256(),
|
||||
key.data(),
|
||||
key.size(),
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
data.size(),
|
||||
reinterpret_cast<unsigned char*>(result.data()),
|
||||
&length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace openssl
|
||||
|
||||
namespace bytes {
|
||||
|
||||
inline void set_random(span destination) {
|
||||
RAND_bytes(
|
||||
reinterpret_cast<unsigned char*>(destination.data()),
|
||||
destination.size());
|
||||
}
|
||||
|
||||
} // namespace bytes
|
|
@ -1,171 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <gsl/gsl_assert>
|
||||
#include "base/variant.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename... Types>
|
||||
class optional_variant {
|
||||
public:
|
||||
optional_variant() : _impl(std::nullopt) {
|
||||
}
|
||||
optional_variant(const optional_variant &other) : _impl(other._impl) {
|
||||
}
|
||||
optional_variant(optional_variant &&other) : _impl(std::move(other._impl)) {
|
||||
}
|
||||
template <typename T, typename = std::enable_if_t<!std::is_base_of<optional_variant, std::decay_t<T>>::value>>
|
||||
optional_variant(T &&value) : _impl(std::forward<T>(value)) {
|
||||
}
|
||||
optional_variant &operator=(const optional_variant &other) {
|
||||
_impl = other._impl;
|
||||
return *this;
|
||||
}
|
||||
optional_variant &operator=(optional_variant &&other) {
|
||||
_impl = std::move(other._impl);
|
||||
return *this;
|
||||
}
|
||||
template <typename T, typename = std::enable_if_t<!std::is_base_of<optional_variant, std::decay_t<T>>::value>>
|
||||
optional_variant &operator=(T &&value) {
|
||||
_impl = std::forward<T>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return !is<std::nullopt_t>();
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return has_value();
|
||||
}
|
||||
bool operator==(const optional_variant &other) const {
|
||||
return _impl == other._impl;
|
||||
}
|
||||
bool operator!=(const optional_variant &other) const {
|
||||
return _impl != other._impl;
|
||||
}
|
||||
bool operator<(const optional_variant &other) const {
|
||||
return _impl < other._impl;
|
||||
}
|
||||
bool operator<=(const optional_variant &other) const {
|
||||
return _impl <= other._impl;
|
||||
}
|
||||
bool operator>(const optional_variant &other) const {
|
||||
return _impl > other._impl;
|
||||
}
|
||||
bool operator>=(const optional_variant &other) const {
|
||||
return _impl >= other._impl;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T &set(Args &&...args) {
|
||||
_impl.template set<T>(std::forward<Args>(args)...);
|
||||
return get_unchecked<T>();
|
||||
}
|
||||
void clear() {
|
||||
_impl.template set<std::nullopt_t>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
decltype(auto) is() const {
|
||||
return _impl.template is<T>();
|
||||
}
|
||||
template <typename T>
|
||||
decltype(auto) get_unchecked() {
|
||||
return _impl.template get_unchecked<T>();
|
||||
}
|
||||
template <typename T>
|
||||
decltype(auto) get_unchecked() const {
|
||||
return _impl.template get_unchecked<T>();
|
||||
}
|
||||
|
||||
template <typename ...Methods>
|
||||
decltype(auto) match(Methods &&...methods) {
|
||||
return base::match(_impl, std::forward<Methods>(methods)...);
|
||||
}
|
||||
template <typename ...Methods>
|
||||
decltype(auto) match(Methods &&...methods) const {
|
||||
return base::match(_impl, std::forward<Methods>(methods)...);
|
||||
}
|
||||
|
||||
private:
|
||||
variant<std::nullopt_t, Types...> _impl;
|
||||
|
||||
};
|
||||
|
||||
template <typename T, typename... Types>
|
||||
inline T *get_if(optional_variant<Types...> *v) {
|
||||
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
|
||||
}
|
||||
|
||||
template <typename T, typename... Types>
|
||||
inline const T *get_if(const optional_variant<Types...> *v) {
|
||||
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
|
||||
}
|
||||
|
||||
template <typename ...Types, typename ...Methods>
|
||||
inline decltype(auto) match(
|
||||
optional_variant<Types...> &value,
|
||||
Methods &&...methods) {
|
||||
return value.match(std::forward<Methods>(methods)...);
|
||||
}
|
||||
|
||||
template <typename ...Types, typename ...Methods>
|
||||
inline decltype(auto) match(
|
||||
const optional_variant<Types...> &value,
|
||||
Methods &&...methods) {
|
||||
return value.match(std::forward<Methods>(methods)...);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
struct optional_wrap_once {
|
||||
using type = std::optional<Type>;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct optional_wrap_once<std::optional<Type>> {
|
||||
using type = std::optional<Type>;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
using optional_wrap_once_t = typename optional_wrap_once<std::decay_t<Type>>::type;
|
||||
|
||||
template <typename Type>
|
||||
struct optional_chain_result {
|
||||
using type = optional_wrap_once_t<Type>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct optional_chain_result<void> {
|
||||
using type = bool;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
using optional_chain_result_t = typename optional_chain_result<Type>::type;
|
||||
|
||||
template <typename Type>
|
||||
optional_wrap_once_t<Type> make_optional(Type &&value) {
|
||||
return optional_wrap_once_t<Type> { std::forward<Type>(value) };
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
template <typename Type, typename Method>
|
||||
inline auto operator|(const std::optional<Type> &value, Method method)
|
||||
-> base::optional_chain_result_t<decltype(method(*value))> {
|
||||
if constexpr (std::is_same_v<decltype(method(*value)), void>) {
|
||||
return value ? (method(*value), true) : false;
|
||||
} else {
|
||||
return value
|
||||
? base::optional_chain_result_t<decltype(method(*value))>(
|
||||
method(*value))
|
||||
: std::nullopt;
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QMap>
|
||||
|
||||
// ordered set template based on QMap
|
||||
template <typename T>
|
||||
class OrderedSet {
|
||||
struct NullType {
|
||||
};
|
||||
using Self = OrderedSet<T>;
|
||||
using Impl = QMap<T, NullType>;
|
||||
using IteratorImpl = typename Impl::iterator;
|
||||
using ConstIteratorImpl = typename Impl::const_iterator;
|
||||
Impl impl_;
|
||||
|
||||
public:
|
||||
OrderedSet() = default;
|
||||
OrderedSet(const OrderedSet &other) = default;
|
||||
OrderedSet(OrderedSet &&other) = default;
|
||||
OrderedSet &operator=(const OrderedSet &other) = default;
|
||||
OrderedSet &operator=(OrderedSet &&other) = default;
|
||||
~OrderedSet() = default;
|
||||
|
||||
inline bool operator==(const Self &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const Self &other) const { return impl_ != other.impl_; }
|
||||
inline int size() const { return impl_.size(); }
|
||||
inline bool isEmpty() const { return impl_.isEmpty(); }
|
||||
inline void detach() { return impl_.detach(); }
|
||||
inline bool isDetached() const { return impl_.isDetached(); }
|
||||
inline void clear() { return impl_.clear(); }
|
||||
inline QList<T> values() const { return impl_.keys(); }
|
||||
inline const T &first() const { return impl_.firstKey(); }
|
||||
inline const T &last() const { return impl_.lastKey(); }
|
||||
|
||||
class const_iterator;
|
||||
class iterator {
|
||||
public:
|
||||
typedef typename IteratorImpl::iterator_category iterator_category;
|
||||
typedef typename IteratorImpl::difference_type difference_type;
|
||||
typedef T value_type;
|
||||
typedef T *pointer;
|
||||
typedef T &reference;
|
||||
|
||||
iterator() = default;
|
||||
iterator(const iterator &other) = default;
|
||||
iterator &operator=(const iterator &other) = default;
|
||||
inline const T &operator*() const { return impl_.key(); }
|
||||
inline const T *operator->() const { return &impl_.key(); }
|
||||
inline bool operator==(const iterator &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const iterator &other) const { return impl_ != other.impl_; }
|
||||
inline iterator &operator++() { ++impl_; return *this; }
|
||||
inline iterator operator++(int) { return iterator(impl_++); }
|
||||
inline iterator &operator--() { --impl_; return *this; }
|
||||
inline iterator operator--(int) { return iterator(impl_--); }
|
||||
inline iterator operator+(int j) const { return iterator(impl_ + j); }
|
||||
inline iterator operator-(int j) const { return iterator(impl_ - j); }
|
||||
inline iterator &operator+=(int j) { impl_ += j; return *this; }
|
||||
inline iterator &operator-=(int j) { impl_ -= j; return *this; }
|
||||
|
||||
friend class const_iterator;
|
||||
inline bool operator==(const const_iterator &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const const_iterator &other) const { return impl_ != other.impl_; }
|
||||
|
||||
private:
|
||||
explicit iterator(const IteratorImpl &impl) : impl_(impl) {
|
||||
}
|
||||
IteratorImpl impl_;
|
||||
friend class OrderedSet<T>;
|
||||
|
||||
};
|
||||
friend class iterator;
|
||||
|
||||
class const_iterator {
|
||||
public:
|
||||
typedef typename IteratorImpl::iterator_category iterator_category;
|
||||
typedef typename IteratorImpl::difference_type difference_type;
|
||||
typedef T value_type;
|
||||
typedef T *pointer;
|
||||
typedef T &reference;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const const_iterator &other) = default;
|
||||
const_iterator &operator=(const const_iterator &other) = default;
|
||||
const_iterator(const iterator &other) : impl_(other.impl_) {
|
||||
}
|
||||
const_iterator &operator=(const iterator &other) {
|
||||
impl_ = other.impl_;
|
||||
return *this;
|
||||
}
|
||||
inline const T &operator*() const { return impl_.key(); }
|
||||
inline const T *operator->() const { return &impl_.key(); }
|
||||
inline bool operator==(const const_iterator &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const const_iterator &other) const { return impl_ != other.impl_; }
|
||||
inline const_iterator &operator++() { ++impl_; return *this; }
|
||||
inline const_iterator operator++(int) { return const_iterator(impl_++); }
|
||||
inline const_iterator &operator--() { --impl_; return *this; }
|
||||
inline const_iterator operator--(int) { return const_iterator(impl_--); }
|
||||
inline const_iterator operator+(int j) const { return const_iterator(impl_ + j); }
|
||||
inline const_iterator operator-(int j) const { return const_iterator(impl_ - j); }
|
||||
inline const_iterator &operator+=(int j) { impl_ += j; return *this; }
|
||||
inline const_iterator &operator-=(int j) { impl_ -= j; return *this; }
|
||||
|
||||
friend class iterator;
|
||||
inline bool operator==(const iterator &other) const { return impl_ == other.impl_; }
|
||||
inline bool operator!=(const iterator &other) const { return impl_ != other.impl_; }
|
||||
|
||||
private:
|
||||
explicit const_iterator(const ConstIteratorImpl &impl) : impl_(impl) {
|
||||
}
|
||||
ConstIteratorImpl impl_;
|
||||
friend class OrderedSet<T>;
|
||||
|
||||
};
|
||||
friend class const_iterator;
|
||||
|
||||
// STL style
|
||||
inline iterator begin() { return iterator(impl_.begin()); }
|
||||
inline const_iterator begin() const { return const_iterator(impl_.cbegin()); }
|
||||
inline const_iterator constBegin() const { return const_iterator(impl_.cbegin()); }
|
||||
inline const_iterator cbegin() const { return const_iterator(impl_.cbegin()); }
|
||||
inline iterator end() { detach(); return iterator(impl_.end()); }
|
||||
inline const_iterator end() const { return const_iterator(impl_.cend()); }
|
||||
inline const_iterator constEnd() const { return const_iterator(impl_.cend()); }
|
||||
inline const_iterator cend() const { return const_iterator(impl_.cend()); }
|
||||
inline iterator erase(iterator it) { return iterator(impl_.erase(it.impl_)); }
|
||||
|
||||
inline iterator insert(const T &value) { return iterator(impl_.insert(value, NullType())); }
|
||||
inline iterator insert(const_iterator pos, const T &value) { return iterator(impl_.insert(pos.impl_, value, NullType())); }
|
||||
inline int remove(const T &value) { return impl_.remove(value); }
|
||||
inline bool contains(const T &value) const { return impl_.contains(value); }
|
||||
|
||||
// more Qt
|
||||
typedef iterator Iterator;
|
||||
typedef const_iterator ConstIterator;
|
||||
inline int count() const { return impl_.count(); }
|
||||
inline iterator find(const T &value) { return iterator(impl_.find(value)); }
|
||||
inline const_iterator find(const T &value) const { return const_iterator(impl_.constFind(value)); }
|
||||
inline const_iterator constFind(const T &value) const { return const_iterator(impl_.constFind(value)); }
|
||||
inline Self &unite(const Self &other) { impl_.unite(other.impl_); return *this; }
|
||||
|
||||
// STL compatibility
|
||||
typedef typename Impl::difference_type difference_type;
|
||||
typedef typename Impl::size_type size_type;
|
||||
inline bool empty() const { return impl_.empty(); }
|
||||
|
||||
};
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
|
||||
// This implementation was taken from range-v3 library.
|
||||
// It was modified so that more than one of passed function objects
|
||||
// could be called with an argument list and the first one that
|
||||
// matches gets used instead of a compile-time ambiguity error.
|
||||
//
|
||||
// This allows to write "default" visitor handlers with [](auto&&) syntax.
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr bool is_callable_v = rpl::details::is_callable_plain_v<Args...>;
|
||||
|
||||
template <typename ...Args>
|
||||
struct overloaded;
|
||||
|
||||
template <>
|
||||
struct overloaded<> {
|
||||
};
|
||||
|
||||
template <typename First, typename ...Rest>
|
||||
struct overloaded<First, Rest...>
|
||||
: private First
|
||||
, private overloaded<Rest...> {
|
||||
|
||||
private:
|
||||
using Others = overloaded<Rest...>;
|
||||
|
||||
public:
|
||||
overloaded() = default;
|
||||
|
||||
constexpr overloaded(First first, Rest... rest)
|
||||
: First(std::move(first))
|
||||
, Others(std::move(rest)...) {
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args)
|
||||
-> decltype(std::declval<First&>()(std::forward<Args>(args)...)) {
|
||||
return static_cast<First&>(*this)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) const
|
||||
-> decltype(std::declval<const First&>()(std::forward<Args>(args)...)) {
|
||||
return static_cast<const First&>(*this)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename... Args,
|
||||
typename = std::enable_if_t<!is_callable_v<First&, Args&&...>>>
|
||||
auto operator()(Args&&... args)
|
||||
-> decltype(std::declval<Others&>()(std::forward<Args>(args)...)) {
|
||||
return static_cast<Others&>(*this)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename... Args,
|
||||
typename = std::enable_if_t<!is_callable_v<const First&, Args&&...>>>
|
||||
auto operator()(Args&&... args) const
|
||||
-> decltype(std::declval<const Others&>()(std::forward<Args>(args)...)) {
|
||||
return static_cast<const Others&>(*this)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Function>
|
||||
Function overload(Function &&function) {
|
||||
return std::forward<Function>(function);
|
||||
}
|
||||
|
||||
template <typename ...Functions, typename = std::enable_if_t<(sizeof...(Functions) > 1)>>
|
||||
auto overload(Functions ...functions) {
|
||||
return details::overloaded<Functions...>(std::move(functions)...);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/parse_helper.h"
|
||||
|
||||
namespace base {
|
||||
namespace parse {
|
||||
|
||||
// inspired by https://github.com/sindresorhus/strip-json-comments
|
||||
QByteArray stripComments(const QByteArray &content) {
|
||||
enum class InsideComment {
|
||||
None,
|
||||
SingleLine,
|
||||
MultiLine,
|
||||
};
|
||||
auto insideComment = InsideComment::None;
|
||||
auto insideString = false;
|
||||
|
||||
QByteArray result;
|
||||
auto begin = content.cbegin(), end = content.cend(), offset = begin;
|
||||
auto feedContent = [&result, &offset, end](const char *ch) {
|
||||
if (ch > offset) {
|
||||
if (result.isEmpty()) result.reserve(end - offset - 2);
|
||||
result.append(offset, ch - offset);
|
||||
offset = ch;
|
||||
}
|
||||
};
|
||||
auto feedComment = [&result, &offset, end](const char *ch) {
|
||||
if (ch > offset) {
|
||||
if (result.isEmpty()) result.reserve(end - offset - 2);
|
||||
result.append(' ');
|
||||
offset = ch;
|
||||
}
|
||||
};
|
||||
for (auto ch = offset; ch != end;) {
|
||||
auto currentChar = *ch;
|
||||
auto nextChar = (ch + 1 == end) ? 0 : *(ch + 1);
|
||||
|
||||
if (insideComment == InsideComment::None && currentChar == '"') {
|
||||
auto escaped = ((ch > begin) && *(ch - 1) == '\\') && ((ch - 1 < begin) || *(ch - 2) != '\\');
|
||||
if (!escaped) {
|
||||
insideString = !insideString;
|
||||
}
|
||||
}
|
||||
if (insideString) {
|
||||
++ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '/') {
|
||||
feedContent(ch);
|
||||
insideComment = InsideComment::SingleLine;
|
||||
ch += 2;
|
||||
} else if (insideComment == InsideComment::SingleLine && currentChar == '\r' && nextChar == '\n') {
|
||||
feedComment(ch);
|
||||
ch += 2;
|
||||
insideComment = InsideComment::None;
|
||||
} else if (insideComment == InsideComment::SingleLine && currentChar == '\n') {
|
||||
feedComment(ch);
|
||||
++ch;
|
||||
insideComment = InsideComment::None;
|
||||
} else if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '*') {
|
||||
feedContent(ch);
|
||||
ch += 2;
|
||||
insideComment = InsideComment::MultiLine;
|
||||
} else if (insideComment == InsideComment::MultiLine && currentChar == '*' && nextChar == '/') {
|
||||
ch += 2;
|
||||
feedComment(ch);
|
||||
insideComment = InsideComment::None;
|
||||
} else if (insideComment == InsideComment::MultiLine && currentChar == '\r' && nextChar == '\n') {
|
||||
feedComment(ch);
|
||||
ch += 2;
|
||||
feedContent(ch);
|
||||
} else if (insideComment == InsideComment::MultiLine && currentChar == '\n') {
|
||||
feedComment(ch);
|
||||
++ch;
|
||||
feedContent(ch);
|
||||
} else {
|
||||
++ch;
|
||||
}
|
||||
}
|
||||
|
||||
if (insideComment == InsideComment::MultiLine) {
|
||||
// unexpected end of content
|
||||
}
|
||||
if (insideComment == InsideComment::None && end > offset) {
|
||||
if (result.isEmpty()) {
|
||||
return content;
|
||||
} else {
|
||||
result.append(offset, end - offset);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace parse
|
||||
} // namespace base
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
namespace parse {
|
||||
|
||||
// Strip all C-style comments.
|
||||
QByteArray stripComments(const QByteArray &content);
|
||||
|
||||
inline bool skipWhitespaces(const char *&from, const char *end) {
|
||||
Assert(from <= end);
|
||||
while (from != end && (
|
||||
(*from == ' ') ||
|
||||
(*from == '\n') ||
|
||||
(*from == '\t') ||
|
||||
(*from == '\r'))) {
|
||||
++from;
|
||||
}
|
||||
return (from != end);
|
||||
}
|
||||
|
||||
inline QLatin1String readName(const char *&from, const char *end) {
|
||||
Assert(from <= end);
|
||||
auto start = from;
|
||||
while (from != end && (
|
||||
(*from >= 'a' && *from <= 'z') ||
|
||||
(*from >= 'A' && *from <= 'Z') ||
|
||||
(*from >= '0' && *from <= '9') ||
|
||||
(*from == '_'))) {
|
||||
++from;
|
||||
}
|
||||
return QLatin1String(start, from - start);
|
||||
}
|
||||
|
||||
} // namespace parse
|
||||
} // namespace base
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/algorithm.h"
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
namespace base {
|
||||
|
||||
class qt_connection final {
|
||||
public:
|
||||
qt_connection(QMetaObject::Connection data = {}) : _data(data) {
|
||||
}
|
||||
qt_connection(qt_connection &&other) : _data(base::take(other._data)) {
|
||||
}
|
||||
qt_connection &operator=(qt_connection &&other) {
|
||||
reset(base::take(other._data));
|
||||
return *this;
|
||||
}
|
||||
~qt_connection() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void release() {
|
||||
_data = QMetaObject::Connection();
|
||||
}
|
||||
void reset(QMetaObject::Connection data = {}) {
|
||||
disconnect();
|
||||
_data = data;
|
||||
}
|
||||
|
||||
private:
|
||||
void disconnect() {
|
||||
if (_data) {
|
||||
QObject::disconnect(base::take(_data));
|
||||
}
|
||||
}
|
||||
|
||||
QMetaObject::Connection _data;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/base_integration.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// This method allows to create an rpl::producer from a Qt object
|
||||
// and a signal with none or one reported value.
|
||||
//
|
||||
// QtSignalProducer(qtWindow, &QWindow::activeChanged) | rpl::start_
|
||||
//
|
||||
// This producer values construct a custom event loop leave point.
|
||||
// This means that all postponeCall's will be invoked right after
|
||||
// the value processing by the current consumer finishes.
|
||||
template <typename Object, typename Signal>
|
||||
auto qt_signal_producer(Object *object, Signal signal);
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Signal>
|
||||
struct qt_signal_argument;
|
||||
|
||||
template <typename Class, typename Return, typename Value>
|
||||
struct qt_signal_argument<Return(Class::*)(Value)> {
|
||||
using type = Value;
|
||||
};
|
||||
|
||||
template <typename Class, typename Return>
|
||||
struct qt_signal_argument<Return(Class::*)()> {
|
||||
using type = void;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Object, typename Signal>
|
||||
auto qt_signal_producer(Object *object, Signal signal) {
|
||||
using Value = typename details::qt_signal_argument<Signal>::type;
|
||||
static constexpr auto NoArgument = std::is_same_v<Value, void>;
|
||||
using Produced = std::conditional_t<
|
||||
NoArgument,
|
||||
rpl::empty_value,
|
||||
std::remove_const_t<std::decay_t<Value>>>;
|
||||
const auto guarded = QPointer<Object>(object);
|
||||
return rpl::make_producer<Produced>([=](auto consumer) {
|
||||
if (!guarded) {
|
||||
return rpl::lifetime();
|
||||
}
|
||||
const auto connect = [&](auto &&handler) {
|
||||
const auto listener = new QObject(guarded.data());
|
||||
QObject::connect(
|
||||
guarded,
|
||||
signal,
|
||||
listener,
|
||||
std::forward<decltype(handler)>(handler));
|
||||
const auto weak = QPointer<QObject>(listener);
|
||||
return rpl::lifetime([=] {
|
||||
if (weak) {
|
||||
delete weak;
|
||||
}
|
||||
});
|
||||
};
|
||||
auto put = [=](const Produced &value) {
|
||||
EnterFromEventLoop([&] {
|
||||
consumer.put_next_copy(value);
|
||||
});
|
||||
};
|
||||
if constexpr (NoArgument) {
|
||||
return connect([put = std::move(put)] { put({}); });
|
||||
} else {
|
||||
return connect(std::move(put));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/flags.h"
|
||||
|
||||
namespace qthelp {
|
||||
|
||||
class RegularExpressionMatch {
|
||||
public:
|
||||
RegularExpressionMatch(const QRegularExpressionMatch &other) = delete;
|
||||
RegularExpressionMatch(const RegularExpressionMatch &other) = delete;
|
||||
RegularExpressionMatch(QRegularExpressionMatch &&match) : data_(std::move(match)) {
|
||||
}
|
||||
RegularExpressionMatch(RegularExpressionMatch &&other) : data_(std::move(other.data_)) {
|
||||
}
|
||||
RegularExpressionMatch &operator=(const QRegularExpressionMatch &match) = delete;
|
||||
RegularExpressionMatch &operator=(const RegularExpressionMatch &other) = delete;
|
||||
RegularExpressionMatch &operator=(QRegularExpressionMatch &&match) {
|
||||
data_ = std::move(match);
|
||||
return *this;
|
||||
}
|
||||
RegularExpressionMatch &operator=(RegularExpressionMatch &&other) {
|
||||
data_ = std::move(other.data_);
|
||||
return *this;
|
||||
}
|
||||
QRegularExpressionMatch *operator->() {
|
||||
return &data_;
|
||||
}
|
||||
const QRegularExpressionMatch *operator->() const {
|
||||
return &data_;
|
||||
}
|
||||
bool valid() const {
|
||||
return data_.hasMatch();
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
|
||||
private:
|
||||
QRegularExpressionMatch data_;
|
||||
|
||||
};
|
||||
|
||||
enum class RegExOption {
|
||||
None = QRegularExpression::NoPatternOption,
|
||||
CaseInsensitive = QRegularExpression::CaseInsensitiveOption,
|
||||
DotMatchesEverything = QRegularExpression::DotMatchesEverythingOption,
|
||||
Multiline = QRegularExpression::MultilineOption,
|
||||
ExtendedSyntax = QRegularExpression::ExtendedPatternSyntaxOption,
|
||||
InvertedGreediness = QRegularExpression::InvertedGreedinessOption,
|
||||
DontCapture = QRegularExpression::DontCaptureOption,
|
||||
UseUnicodeProperties = QRegularExpression::UseUnicodePropertiesOption,
|
||||
#ifndef OS_MAC_OLD
|
||||
OptimizeOnFirstUsage = QRegularExpression::OptimizeOnFirstUsageOption,
|
||||
DontAutomaticallyOptimize = QRegularExpression::DontAutomaticallyOptimizeOption,
|
||||
#endif // OS_MAC_OLD
|
||||
};
|
||||
using RegExOptions = base::flags<RegExOption>;
|
||||
inline constexpr auto is_flag_type(RegExOption) { return true; };
|
||||
|
||||
inline RegularExpressionMatch regex_match(const QString &string, const QString &subject, RegExOptions options = 0) {
|
||||
auto qtOptions = QRegularExpression::PatternOptions(static_cast<int>(options));
|
||||
return RegularExpressionMatch(QRegularExpression(string, qtOptions).match(subject));
|
||||
}
|
||||
|
||||
inline RegularExpressionMatch regex_match(const QString &string, const QStringRef &subjectRef, RegExOptions options = 0) {
|
||||
auto qtOptions = QRegularExpression::PatternOptions(static_cast<int>(options));
|
||||
#ifndef OS_MAC_OLD
|
||||
return RegularExpressionMatch(QRegularExpression(string, qtOptions).match(subjectRef));
|
||||
#else // OS_MAC_OLD
|
||||
return RegularExpressionMatch(QRegularExpression(string, qtOptions).match(subjectRef.toString()));
|
||||
#endif // OS_MAC_OLD
|
||||
}
|
||||
|
||||
} // namespace qthelp
|
|
@ -1,125 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/qthelp_url.h"
|
||||
|
||||
namespace qthelp {
|
||||
namespace {
|
||||
|
||||
QRegularExpression CreateRegExp(const QString &expression) {
|
||||
auto result = QRegularExpression(
|
||||
expression,
|
||||
QRegularExpression::UseUnicodePropertiesOption);
|
||||
#ifndef OS_MAC_OLD
|
||||
result.optimize();
|
||||
#endif // OS_MAC_OLD
|
||||
return result;
|
||||
}
|
||||
|
||||
QString ExpressionDomain() {
|
||||
// Matches any domain name, containing at least one '.', including "file.txt".
|
||||
return QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)?((?:[A-Za-z" "\xD0\x90-\xD0\xAF\xD0\x81" "\xD0\xB0-\xD1\x8F\xD1\x91" "0-9\\-\\_]+\\.){1,10}([A-Za-z" "\xD1\x80\xD1\x84" "\\-\\d]{2,22})(\\:\\d+)?)");
|
||||
}
|
||||
|
||||
QString ExpressionDomainExplicit() {
|
||||
// Matches any domain name, containing a protocol, including "test://localhost".
|
||||
return QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)((?:[A-Za-z" "\xD0\x90-\xD0\xAF\xD0\x81" "\xD0\xB0-\xD1\x8F\xD1\x91" "0-9\\-\\_]+\\.){0,10}([A-Za-z" "\xD1\x80\xD1\x84" "\\-\\d]{2,22})(\\:\\d+)?)");
|
||||
}
|
||||
|
||||
bool IsGoodProtocol(const QString &protocol) {
|
||||
const auto equals = [&](QLatin1String string) {
|
||||
return protocol.compare(string, Qt::CaseInsensitive) == 0;
|
||||
};
|
||||
return equals(qstr("http"))
|
||||
|| equals(qstr("https"))
|
||||
|| equals(qstr("tg"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const QRegularExpression &RegExpDomain() {
|
||||
static const auto result = CreateRegExp(ExpressionDomain());
|
||||
return result;
|
||||
}
|
||||
|
||||
const QRegularExpression &RegExpDomainExplicit() {
|
||||
static const auto result = CreateRegExp(ExpressionDomainExplicit());
|
||||
return result;
|
||||
}
|
||||
|
||||
QRegularExpression RegExpProtocol() {
|
||||
static const auto result = CreateRegExp("^([a-zA-Z]+)://");
|
||||
return result;
|
||||
}
|
||||
|
||||
QMap<QString, QString> url_parse_params(
|
||||
const QString ¶ms,
|
||||
UrlParamNameTransform transform) {
|
||||
auto result = QMap<QString, QString>();
|
||||
|
||||
const auto transformParamName = [transform](const QString &name) {
|
||||
if (transform == UrlParamNameTransform::ToLower) {
|
||||
return name.toLower();
|
||||
}
|
||||
return name;
|
||||
};
|
||||
for (const auto ¶m : params.split('&')) {
|
||||
// Skip params without a name (starting with '=').
|
||||
if (auto separatorPosition = param.indexOf('=')) {
|
||||
const auto paramName = transformParamName(
|
||||
(separatorPosition > 0)
|
||||
? param.mid(0, separatorPosition)
|
||||
: param);
|
||||
const auto paramValue = (separatorPosition > 0)
|
||||
? url_decode(param.mid(separatorPosition + 1))
|
||||
: QString();
|
||||
if (!result.contains(paramName)) {
|
||||
result.insert(paramName, paramValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_ipv6(const QString &ip) {
|
||||
//static const auto regexp = QRegularExpression("^[a-fA-F0-9:]+$");
|
||||
//return regexp.match(ip).hasMatch();
|
||||
return ip.indexOf('.') < 0 && ip.indexOf(':') >= 0;
|
||||
}
|
||||
|
||||
QString url_append_query_or_hash(const QString &url, const QString &add) {
|
||||
const auto query = url.lastIndexOf('?');
|
||||
if (query < 0) {
|
||||
return url + '?' + add;
|
||||
}
|
||||
const auto hash = url.lastIndexOf('#');
|
||||
return url
|
||||
+ (query >= 0 && query > url.lastIndexOf('#') ? '&' : '?')
|
||||
+ add;
|
||||
}
|
||||
|
||||
QString validate_url(const QString &value) {
|
||||
const auto trimmed = value.trimmed();
|
||||
if (trimmed.isEmpty()) {
|
||||
return QString();
|
||||
}
|
||||
const auto match = RegExpDomainExplicit().match(trimmed);
|
||||
if (!match.hasMatch()) {
|
||||
const auto domain = RegExpDomain().match(trimmed);
|
||||
if (!domain.hasMatch() || domain.capturedStart() != 0) {
|
||||
return QString();
|
||||
}
|
||||
return qstr("http://") + trimmed;
|
||||
} else if (match.capturedStart() != 0) {
|
||||
return QString();
|
||||
}
|
||||
const auto protocolMatch = RegExpProtocol().match(trimmed);
|
||||
Assert(protocolMatch.hasMatch());
|
||||
return IsGoodProtocol(protocolMatch.captured(1)) ? trimmed : QString();
|
||||
}
|
||||
|
||||
} // namespace qthelp
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QRegularExpression>
|
||||
|
||||
namespace qthelp {
|
||||
|
||||
const QRegularExpression &RegExpDomain();
|
||||
const QRegularExpression &RegExpDomainExplicit();
|
||||
QRegularExpression RegExpProtocol();
|
||||
|
||||
inline QString url_encode(const QString &part) {
|
||||
return QString::fromLatin1(QUrl::toPercentEncoding(part));
|
||||
}
|
||||
|
||||
inline QString url_decode(const QString &encoded) {
|
||||
return QUrl::fromPercentEncoding(encoded.toUtf8());
|
||||
}
|
||||
|
||||
enum class UrlParamNameTransform {
|
||||
NoTransform,
|
||||
ToLower,
|
||||
};
|
||||
// Parses a string like "p1=v1&p2=v2&..&pn=vn" to a map.
|
||||
QMap<QString, QString> url_parse_params(
|
||||
const QString ¶ms,
|
||||
UrlParamNameTransform transform = UrlParamNameTransform::NoTransform);
|
||||
|
||||
QString url_append_query_or_hash(const QString &url, const QString &add);
|
||||
|
||||
bool is_ipv6(const QString &ip);
|
||||
|
||||
QString validate_url(const QString &value);
|
||||
|
||||
} // namespace qthelp
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/runtime_composer.h"
|
||||
|
||||
struct RuntimeComposerMetadatasMap {
|
||||
std::map<uint64, std::unique_ptr<RuntimeComposerMetadata>> data;
|
||||
QMutex mutex;
|
||||
};
|
||||
|
||||
const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask) {
|
||||
static RuntimeComposerMetadatasMap RuntimeComposerMetadatas;
|
||||
|
||||
QMutexLocker lock(&RuntimeComposerMetadatas.mutex);
|
||||
auto i = RuntimeComposerMetadatas.data.find(mask);
|
||||
if (i == end(RuntimeComposerMetadatas.data)) {
|
||||
i = RuntimeComposerMetadatas.data.emplace(
|
||||
mask,
|
||||
std::make_unique<RuntimeComposerMetadata>(mask)).first;
|
||||
}
|
||||
return i->second.get();
|
||||
}
|
||||
|
||||
const RuntimeComposerMetadata *RuntimeComposerBase::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0);
|
||||
|
||||
RuntimeComponentWrapStruct RuntimeComponentWraps[64];
|
||||
|
||||
QAtomicInt RuntimeComponentIndexLast;
|
|
@ -1,279 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
template <typename Base>
|
||||
class RuntimeComposer;
|
||||
|
||||
class RuntimeComposerBase;
|
||||
typedef void(*RuntimeComponentConstruct)(void *location, RuntimeComposerBase *composer);
|
||||
typedef void(*RuntimeComponentDestruct)(void *location);
|
||||
typedef void(*RuntimeComponentMove)(void *location, void *waslocation);
|
||||
|
||||
struct RuntimeComponentWrapStruct {
|
||||
// Don't init any fields, because it is only created in
|
||||
// global scope, so it will be filled by zeros from the start.
|
||||
RuntimeComponentWrapStruct() = default;
|
||||
RuntimeComponentWrapStruct(std::size_t size, std::size_t align, RuntimeComponentConstruct construct, RuntimeComponentDestruct destruct, RuntimeComponentMove move)
|
||||
: Size(size)
|
||||
, Align(align)
|
||||
, Construct(construct)
|
||||
, Destruct(destruct)
|
||||
, Move(move) {
|
||||
}
|
||||
std::size_t Size;
|
||||
std::size_t Align;
|
||||
RuntimeComponentConstruct Construct;
|
||||
RuntimeComponentDestruct Destruct;
|
||||
RuntimeComponentMove Move;
|
||||
};
|
||||
|
||||
template <int Value, int Denominator>
|
||||
struct CeilDivideMinimumOne {
|
||||
static constexpr int Result = ((Value / Denominator) + ((!Value || (Value % Denominator)) ? 1 : 0));
|
||||
};
|
||||
|
||||
extern RuntimeComponentWrapStruct RuntimeComponentWraps[64];
|
||||
extern QAtomicInt RuntimeComponentIndexLast;
|
||||
|
||||
template <typename Type, typename Base>
|
||||
struct RuntimeComponent {
|
||||
using RuntimeComponentBase = Base;
|
||||
|
||||
RuntimeComponent() {
|
||||
// While there is no std::aligned_alloc().
|
||||
static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!");
|
||||
}
|
||||
RuntimeComponent(const RuntimeComponent &other) = delete;
|
||||
RuntimeComponent &operator=(const RuntimeComponent &other) = delete;
|
||||
RuntimeComponent(RuntimeComponent &&other) = delete;
|
||||
RuntimeComponent &operator=(RuntimeComponent &&other) = default;
|
||||
|
||||
static int Index() {
|
||||
static QAtomicInt MyIndex(0);
|
||||
if (auto index = MyIndex.loadAcquire()) {
|
||||
return index - 1;
|
||||
}
|
||||
while (true) {
|
||||
auto last = RuntimeComponentIndexLast.loadAcquire();
|
||||
if (RuntimeComponentIndexLast.testAndSetOrdered(last, last + 1)) {
|
||||
Assert(last < 64);
|
||||
if (MyIndex.testAndSetOrdered(0, last + 1)) {
|
||||
RuntimeComponentWraps[last] = RuntimeComponentWrapStruct(
|
||||
sizeof(Type),
|
||||
alignof(Type),
|
||||
Type::RuntimeComponentConstruct,
|
||||
Type::RuntimeComponentDestruct,
|
||||
Type::RuntimeComponentMove);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return MyIndex.loadAcquire() - 1;
|
||||
}
|
||||
static uint64 Bit() {
|
||||
return (1ULL << Index());
|
||||
}
|
||||
|
||||
protected:
|
||||
static void RuntimeComponentConstruct(void *location, RuntimeComposerBase *composer) {
|
||||
new (location) Type();
|
||||
}
|
||||
static void RuntimeComponentDestruct(void *location) {
|
||||
((Type*)location)->~Type();
|
||||
}
|
||||
static void RuntimeComponentMove(void *location, void *waslocation) {
|
||||
*(Type*)location = std::move(*(Type*)waslocation);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class RuntimeComposerMetadata {
|
||||
public:
|
||||
RuntimeComposerMetadata(uint64 mask) : _mask(mask) {
|
||||
for (int i = 0; i != 64; ++i) {
|
||||
auto componentBit = (1ULL << i);
|
||||
if (_mask & componentBit) {
|
||||
auto componentSize = RuntimeComponentWraps[i].Size;
|
||||
if (componentSize) {
|
||||
auto componentAlign = RuntimeComponentWraps[i].Align;
|
||||
if (auto badAlign = (size % componentAlign)) {
|
||||
size += (componentAlign - badAlign);
|
||||
}
|
||||
offsets[i] = size;
|
||||
size += componentSize;
|
||||
accumulate_max(align, componentAlign);
|
||||
}
|
||||
} else if (_mask < componentBit) {
|
||||
last = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Meta pointer in the start.
|
||||
std::size_t size = sizeof(const RuntimeComposerMetadata*);
|
||||
std::size_t align = alignof(const RuntimeComposerMetadata*);
|
||||
std::size_t offsets[64] = { 0 };
|
||||
int last = 64;
|
||||
|
||||
bool equals(uint64 mask) const {
|
||||
return _mask == mask;
|
||||
}
|
||||
uint64 maskadd(uint64 mask) const {
|
||||
return _mask | mask;
|
||||
}
|
||||
uint64 maskremove(uint64 mask) const {
|
||||
return _mask & (~mask);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64 _mask;
|
||||
|
||||
};
|
||||
|
||||
const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask);
|
||||
|
||||
class RuntimeComposerBase {
|
||||
public:
|
||||
RuntimeComposerBase(uint64 mask = 0) : _data(zerodata()) {
|
||||
if (mask) {
|
||||
auto meta = GetRuntimeComposerMetadata(mask);
|
||||
|
||||
auto data = operator new(meta->size);
|
||||
Assert(data != nullptr);
|
||||
|
||||
_data = data;
|
||||
_meta() = meta;
|
||||
for (int i = 0; i < meta->last; ++i) {
|
||||
auto offset = meta->offsets[i];
|
||||
if (offset >= sizeof(_meta())) {
|
||||
try {
|
||||
auto constructAt = _dataptrunsafe(offset);
|
||||
auto space = RuntimeComponentWraps[i].Size;
|
||||
auto alignedAt = constructAt;
|
||||
std::align(RuntimeComponentWraps[i].Align, space, alignedAt, space);
|
||||
Assert(alignedAt == constructAt);
|
||||
RuntimeComponentWraps[i].Construct(constructAt, this);
|
||||
} catch (...) {
|
||||
while (i > 0) {
|
||||
--i;
|
||||
offset = meta->offsets[--i];
|
||||
if (offset >= sizeof(_meta())) {
|
||||
RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset));
|
||||
}
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RuntimeComposerBase(const RuntimeComposerBase &other) = delete;
|
||||
RuntimeComposerBase &operator=(const RuntimeComposerBase &other) = delete;
|
||||
~RuntimeComposerBase() {
|
||||
if (_data != zerodata()) {
|
||||
auto meta = _meta();
|
||||
for (int i = 0; i < meta->last; ++i) {
|
||||
auto offset = meta->offsets[i];
|
||||
if (offset >= sizeof(_meta())) {
|
||||
RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset));
|
||||
}
|
||||
}
|
||||
operator delete(_data);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
bool UpdateComponents(uint64 mask = 0) {
|
||||
if (_meta()->equals(mask)) {
|
||||
return false;
|
||||
}
|
||||
RuntimeComposerBase result(mask);
|
||||
result.swap(*this);
|
||||
if (_data != zerodata() && result._data != zerodata()) {
|
||||
const auto meta = _meta();
|
||||
const auto wasmeta = result._meta();
|
||||
for (auto i = 0; i != meta->last; ++i) {
|
||||
const auto offset = meta->offsets[i];
|
||||
const auto wasoffset = wasmeta->offsets[i];
|
||||
if (offset >= sizeof(_meta())
|
||||
&& wasoffset >= sizeof(_meta())) {
|
||||
RuntimeComponentWraps[i].Move(
|
||||
_dataptrunsafe(offset),
|
||||
result._dataptrunsafe(wasoffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool AddComponents(uint64 mask = 0) {
|
||||
return UpdateComponents(_meta()->maskadd(mask));
|
||||
}
|
||||
bool RemoveComponents(uint64 mask = 0) {
|
||||
return UpdateComponents(_meta()->maskremove(mask));
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Base>
|
||||
friend class RuntimeComposer;
|
||||
|
||||
static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata;
|
||||
static void *zerodata() {
|
||||
return &ZeroRuntimeComposerMetadata;
|
||||
}
|
||||
|
||||
void *_dataptrunsafe(int skip) const {
|
||||
return (char*)_data + skip;
|
||||
}
|
||||
void *_dataptr(int skip) const {
|
||||
return (skip >= sizeof(_meta())) ? _dataptrunsafe(skip) : nullptr;
|
||||
}
|
||||
const RuntimeComposerMetadata *&_meta() const {
|
||||
return *static_cast<const RuntimeComposerMetadata**>(_data);
|
||||
}
|
||||
void *_data = nullptr;
|
||||
|
||||
void swap(RuntimeComposerBase &other) {
|
||||
std::swap(_data, other._data);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
class RuntimeComposer : public RuntimeComposerBase {
|
||||
public:
|
||||
using RuntimeComposerBase::RuntimeComposerBase;
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
typename = std::enable_if_t<std::is_same_v<
|
||||
typename Type::RuntimeComponentBase,
|
||||
Base>>>
|
||||
bool Has() const {
|
||||
return (_meta()->offsets[Type::Index()] >= sizeof(_meta()));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
typename = std::enable_if_t<std::is_same_v<
|
||||
typename Type::RuntimeComponentBase,
|
||||
Base>>>
|
||||
Type *Get() {
|
||||
return static_cast<Type*>(_dataptr(_meta()->offsets[Type::Index()]));
|
||||
}
|
||||
template <
|
||||
typename Type,
|
||||
typename = std::enable_if_t<std::is_same_v<
|
||||
typename Type::RuntimeComponentBase,
|
||||
Base>>>
|
||||
const Type *Get() const {
|
||||
return static_cast<const Type*>(_dataptr(_meta()->offsets[Type::Index()]));
|
||||
}
|
||||
|
||||
};
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "catch.hpp"
|
||||
#include "reporters/catch_reporter_compact.hpp"
|
||||
#include <QFile>
|
||||
|
||||
int (*TestForkedMethod)()/* = nullptr*/;
|
||||
|
||||
namespace base {
|
||||
namespace assertion {
|
||||
|
||||
// For Assert() / Expects() / Ensures() / Unexpected() to work.
|
||||
void log(const char *message, const char *file, int line) {
|
||||
std::cout << message << " (" << file << ":" << line << ")" << std::endl;
|
||||
}
|
||||
|
||||
} // namespace assertion
|
||||
} // namespace base
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct MinimalReporter : CompactReporter {
|
||||
MinimalReporter( ReporterConfig const& _config )
|
||||
: CompactReporter( _config )
|
||||
{}
|
||||
|
||||
virtual void testRunEnded( TestRunStats const& _testRunStats ) {
|
||||
printTotals( _testRunStats.totals );
|
||||
}
|
||||
|
||||
private:
|
||||
// Colour, message variants:
|
||||
// - white: No tests ran.
|
||||
// - red: Failed [both/all] N test cases, failed [both/all] M assertions.
|
||||
// - white: Passed [both/all] N test cases (no assertions).
|
||||
// - red: Failed N tests cases, failed M assertions.
|
||||
// - green: Passed [both/all] N tests cases with M assertions.
|
||||
|
||||
std::string bothOrAll( std::size_t count ) const {
|
||||
return count == 1 ? std::string() : count == 2 ? "both " : "all " ;
|
||||
}
|
||||
|
||||
void printTotals( const Totals& totals ) const {
|
||||
if( totals.testCases.total() == 0 ) {
|
||||
}
|
||||
else if( totals.testCases.failed == totals.testCases.total() ) {
|
||||
Colour colour( Colour::ResultError );
|
||||
const std::string qualify_assertions_failed =
|
||||
totals.assertions.failed == totals.assertions.total() ?
|
||||
bothOrAll( totals.assertions.failed ) : std::string();
|
||||
stream <<
|
||||
"Failed " << bothOrAll( totals.testCases.failed )
|
||||
<< pluralise( totals.testCases.failed, "test case" ) << ", "
|
||||
"failed " << qualify_assertions_failed <<
|
||||
pluralise( totals.assertions.failed, "assertion" ) << '.';
|
||||
}
|
||||
else if( totals.assertions.total() == 0 ) {
|
||||
stream <<
|
||||
"Passed " << bothOrAll( totals.testCases.total() )
|
||||
<< pluralise( totals.testCases.total(), "test case" )
|
||||
<< " (no assertions).";
|
||||
}
|
||||
else if( totals.assertions.failed ) {
|
||||
Colour colour( Colour::ResultError );
|
||||
stream <<
|
||||
"Failed " << pluralise( totals.testCases.failed, "test case" ) << ", "
|
||||
"failed " << pluralise( totals.assertions.failed, "assertion" ) << '.';
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
INTERNAL_CATCH_REGISTER_REPORTER( "minimal", MinimalReporter )
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
auto touchFile = QString();
|
||||
for (auto i = 0; i != argc; ++i) {
|
||||
if (argv[i] == QString("--touch") && i + 1 != argc) {
|
||||
touchFile = QFile::decodeName(argv[++i]);
|
||||
} else if (argv[i] == QString("--forked") && TestForkedMethod) {
|
||||
return TestForkedMethod();
|
||||
}
|
||||
}
|
||||
const char *catch_argv[] = {
|
||||
argv[0],
|
||||
touchFile.isEmpty() ? "-b" : "-r",
|
||||
touchFile.isEmpty() ? "-b" : "minimal" };
|
||||
constexpr auto catch_argc = sizeof(catch_argv) / sizeof(catch_argv[0]);
|
||||
auto result = Catch::Session().run(catch_argc, catch_argv);
|
||||
if (result == 0 && !touchFile.isEmpty()) {
|
||||
QFile(touchFile).open(QIODevice::WriteOnly);
|
||||
}
|
||||
return (result < 0xff ? result : 0xff);
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename T>
|
||||
class thread_safe_wrap {
|
||||
public:
|
||||
template <typename ...Args>
|
||||
thread_safe_wrap(Args &&...args) : _value(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
auto with(Callback &&callback) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
return callback(_value);
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
auto with(Callback &&callback) const {
|
||||
QMutexLocker lock(&_mutex);
|
||||
return callback(_value);
|
||||
}
|
||||
|
||||
private:
|
||||
T _value;
|
||||
QMutex _mutex;
|
||||
|
||||
};
|
||||
|
||||
template <typename T, template<typename...> typename Container = std::deque>
|
||||
class thread_safe_queue {
|
||||
public:
|
||||
template <typename ...Args>
|
||||
void emplace(Args &&...args) {
|
||||
_wrap.with([&](Container<T> &value) {
|
||||
value.emplace_back(std::forward<Args>(args)...);
|
||||
});
|
||||
}
|
||||
|
||||
Container<T> take() {
|
||||
return _wrap.with([&](Container<T> &value) {
|
||||
return std::exchange(value, Container<T>());
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
thread_safe_wrap<Container<T>> _wrap;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/timer.h"
|
||||
|
||||
#include <QtCore/QTimerEvent>
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
QObject *TimersAdjuster() {
|
||||
static QObject adjuster;
|
||||
return &adjuster;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Timer::Timer(
|
||||
not_null<QThread*> thread,
|
||||
Fn<void()> callback)
|
||||
: Timer(std::move(callback)) {
|
||||
moveToThread(thread);
|
||||
}
|
||||
|
||||
Timer::Timer(Fn<void()> callback)
|
||||
: QObject(nullptr)
|
||||
, _callback(std::move(callback))
|
||||
, _type(Qt::PreciseTimer)
|
||||
, _adjusted(false) {
|
||||
setRepeat(Repeat::Interval);
|
||||
connect(
|
||||
TimersAdjuster(),
|
||||
&QObject::destroyed,
|
||||
this,
|
||||
[this] { adjust(); },
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void Timer::start(crl::time timeout, Qt::TimerType type, Repeat repeat) {
|
||||
cancel();
|
||||
|
||||
_type = type;
|
||||
setRepeat(repeat);
|
||||
_adjusted = false;
|
||||
setTimeout(timeout);
|
||||
_timerId = startTimer(_timeout, _type);
|
||||
if (_timerId) {
|
||||
_next = crl::now() + _timeout;
|
||||
} else {
|
||||
_next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::cancel() {
|
||||
if (isActive()) {
|
||||
killTimer(base::take(_timerId));
|
||||
}
|
||||
}
|
||||
|
||||
crl::time Timer::remainingTime() const {
|
||||
if (!isActive()) {
|
||||
return -1;
|
||||
}
|
||||
const auto now = crl::now();
|
||||
return (_next > now) ? (_next - now) : crl::time(0);
|
||||
}
|
||||
|
||||
void Timer::Adjust() {
|
||||
QObject emitter;
|
||||
connect(
|
||||
&emitter,
|
||||
&QObject::destroyed,
|
||||
TimersAdjuster(),
|
||||
&QObject::destroyed);
|
||||
}
|
||||
|
||||
void Timer::adjust() {
|
||||
auto remaining = remainingTime();
|
||||
if (remaining >= 0) {
|
||||
cancel();
|
||||
_timerId = startTimer(remaining, _type);
|
||||
_adjusted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::setTimeout(crl::time timeout) {
|
||||
Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
|
||||
|
||||
_timeout = static_cast<unsigned int>(timeout);
|
||||
}
|
||||
|
||||
int Timer::timeout() const {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
void Timer::timerEvent(QTimerEvent *e) {
|
||||
if (repeat() == Repeat::Interval) {
|
||||
if (_adjusted) {
|
||||
start(_timeout, _type, repeat());
|
||||
} else {
|
||||
_next = crl::now() + _timeout;
|
||||
}
|
||||
} else {
|
||||
cancel();
|
||||
}
|
||||
|
||||
if (const auto onstack = _callback) {
|
||||
onstack();
|
||||
}
|
||||
}
|
||||
|
||||
int DelayedCallTimer::call(
|
||||
crl::time timeout,
|
||||
FnMut<void()> callback,
|
||||
Qt::TimerType type) {
|
||||
Expects(timeout >= 0);
|
||||
|
||||
if (!callback) {
|
||||
return 0;
|
||||
}
|
||||
auto timerId = startTimer(static_cast<int>(timeout), type);
|
||||
if (timerId) {
|
||||
_callbacks.emplace(timerId, std::move(callback));
|
||||
}
|
||||
return timerId;
|
||||
}
|
||||
|
||||
void DelayedCallTimer::cancel(int callId) {
|
||||
if (callId) {
|
||||
killTimer(callId);
|
||||
_callbacks.remove(callId);
|
||||
}
|
||||
}
|
||||
|
||||
void DelayedCallTimer::timerEvent(QTimerEvent *e) {
|
||||
auto timerId = e->timerId();
|
||||
killTimer(timerId);
|
||||
|
||||
auto it = _callbacks.find(timerId);
|
||||
if (it != _callbacks.end()) {
|
||||
auto callback = std::move(it->second);
|
||||
_callbacks.erase(it);
|
||||
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QThread>
|
||||
#include "base/flat_map.h"
|
||||
|
||||
#include <crl/crl_time.h>
|
||||
|
||||
namespace base {
|
||||
|
||||
class Timer final : private QObject {
|
||||
public:
|
||||
explicit Timer(
|
||||
not_null<QThread*> thread,
|
||||
Fn<void()> callback = nullptr);
|
||||
explicit Timer(Fn<void()> callback = nullptr);
|
||||
|
||||
static Qt::TimerType DefaultType(crl::time timeout) {
|
||||
constexpr auto kThreshold = crl::time(1000);
|
||||
return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer;
|
||||
}
|
||||
|
||||
void setCallback(Fn<void()> callback) {
|
||||
_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void callOnce(crl::time timeout) {
|
||||
callOnce(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callEach(crl::time timeout) {
|
||||
callEach(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callOnce(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::SingleShot);
|
||||
}
|
||||
|
||||
void callEach(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::Interval);
|
||||
}
|
||||
|
||||
bool isActive() const {
|
||||
return (_timerId != 0);
|
||||
}
|
||||
|
||||
void cancel();
|
||||
crl::time remainingTime() const;
|
||||
|
||||
static void Adjust();
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *e) override;
|
||||
|
||||
private:
|
||||
enum class Repeat : unsigned {
|
||||
Interval = 0,
|
||||
SingleShot = 1,
|
||||
};
|
||||
void start(crl::time timeout, Qt::TimerType type, Repeat repeat);
|
||||
void adjust();
|
||||
|
||||
void setTimeout(crl::time timeout);
|
||||
int timeout() const;
|
||||
|
||||
void setRepeat(Repeat repeat) {
|
||||
_repeat = static_cast<unsigned>(repeat);
|
||||
}
|
||||
Repeat repeat() const {
|
||||
return static_cast<Repeat>(_repeat);
|
||||
}
|
||||
|
||||
Fn<void()> _callback;
|
||||
crl::time _next = 0;
|
||||
int _timeout = 0;
|
||||
int _timerId = 0;
|
||||
|
||||
Qt::TimerType _type : 2;
|
||||
bool _adjusted : 1;
|
||||
unsigned _repeat : 1;
|
||||
|
||||
};
|
||||
|
||||
class DelayedCallTimer final : private QObject {
|
||||
public:
|
||||
int call(crl::time timeout, FnMut<void()> callback) {
|
||||
return call(
|
||||
timeout,
|
||||
std::move(callback),
|
||||
Timer::DefaultType(timeout));
|
||||
}
|
||||
|
||||
int call(
|
||||
crl::time timeout,
|
||||
FnMut<void()> callback,
|
||||
Qt::TimerType type);
|
||||
void cancel(int callId);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *e) override;
|
||||
|
||||
private:
|
||||
base::flat_map<int, FnMut<void()>> _callbacks;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename T>
|
||||
struct custom_is_fast_copy_type : public std::false_type {
|
||||
};
|
||||
// To make your own type a fast copy type just write:
|
||||
// template <>
|
||||
// struct base::custom_is_fast_copy_type<MyTinyType> : public std::true_type {
|
||||
// };
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename ...Types>
|
||||
struct type_list_contains;
|
||||
|
||||
template <typename T>
|
||||
struct type_list_contains<T> : public std::false_type {
|
||||
};
|
||||
|
||||
template <typename T, typename Head, typename ...Types>
|
||||
struct type_list_contains<T, Head, Types...> : public std::integral_constant<bool, std::is_same<Head, T>::value || type_list_contains<T, Types...>::value> {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using is_std_unsigned_int = type_list_contains<T, unsigned char, unsigned short int, unsigned int, unsigned long int>;
|
||||
|
||||
template <typename T>
|
||||
using is_std_signed_int = type_list_contains<T, signed char, short int, int, long int>;
|
||||
|
||||
template <typename T>
|
||||
using is_std_integral = std::integral_constant<bool, is_std_unsigned_int<T>::value || is_std_signed_int<T>::value || type_list_contains<T, bool, char, wchar_t>::value>;
|
||||
|
||||
template <typename T>
|
||||
using is_std_float = type_list_contains<T, float, double, long double>;
|
||||
|
||||
template <typename T>
|
||||
using is_std_arith = std::integral_constant<bool, is_std_integral<T>::value || is_std_float<T>::value>;
|
||||
|
||||
template <typename T>
|
||||
using is_std_fundamental = std::integral_constant<bool, is_std_arith<T>::value || std::is_same<T, void>::value>;
|
||||
|
||||
template <typename T>
|
||||
struct is_pointer : public std::false_type {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_pointer<T*> : public std::true_type {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_member_pointer : public std::false_type {
|
||||
};
|
||||
|
||||
template <typename T, typename C>
|
||||
struct is_member_pointer<T C::*> : public std::true_type {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using is_fast_copy_type = std::integral_constant<bool, is_std_fundamental<T>::value || is_pointer<T>::value || is_member_pointer<T>::value || custom_is_fast_copy_type<T>::value>;
|
||||
|
||||
template <typename T>
|
||||
struct add_const_reference {
|
||||
using type = const T &;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct add_const_reference<void> {
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using add_const_reference_t = typename add_const_reference<T>::type;
|
||||
|
||||
template <typename T>
|
||||
struct remove_pointer {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct remove_pointer<T*> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using remove_pointer_t = typename remove_pointer<T>::type;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
struct type_traits {
|
||||
using is_std_unsigned_int = internal::is_std_unsigned_int<T>;
|
||||
using is_std_signed_int = internal::is_std_signed_int<T>;
|
||||
using is_std_integral = internal::is_std_integral<T>;
|
||||
using is_std_float = internal::is_std_float<T>;
|
||||
using is_std_arith = internal::is_std_arith<T>;
|
||||
using is_std_fundamental = internal::is_std_fundamental<T>;
|
||||
using is_pointer = internal::is_pointer<T>;
|
||||
using is_member_pointer = internal::is_member_pointer<T>;
|
||||
using is_fast_copy_type = internal::is_fast_copy_type<T>;
|
||||
|
||||
using parameter_type = std::conditional_t<is_fast_copy_type::value, T, internal::add_const_reference_t<T>>;
|
||||
using pointed_type = internal::remove_pointer_t<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using parameter_type = typename type_traits<T>::parameter_type;
|
||||
|
||||
} // namespace base
|
|
@ -1,230 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <any>
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
|
||||
template <typename Value>
|
||||
struct moveable_as_copyable_wrap {
|
||||
moveable_as_copyable_wrap(Value &&other)
|
||||
: value(std::move(other)) {
|
||||
}
|
||||
moveable_as_copyable_wrap &operator=(Value &&other) {
|
||||
value = std::move(other);
|
||||
return *this;
|
||||
}
|
||||
moveable_as_copyable_wrap(moveable_as_copyable_wrap &&other)
|
||||
: value(std::move(other.value)) {
|
||||
}
|
||||
moveable_as_copyable_wrap(
|
||||
const moveable_as_copyable_wrap &other) {
|
||||
Unexpected("Attempt to copy-construct a move-only type.");
|
||||
}
|
||||
moveable_as_copyable_wrap &operator=(
|
||||
moveable_as_copyable_wrap &&other) {
|
||||
value = std::move(other.value);
|
||||
return *this;
|
||||
}
|
||||
moveable_as_copyable_wrap &operator=(
|
||||
const moveable_as_copyable_wrap &other) {
|
||||
Unexpected("Attempt to copy-assign a move-only type.");
|
||||
}
|
||||
|
||||
Value value;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename = std::enable_if_t<
|
||||
std::is_move_constructible_v<std::decay_t<Value>>
|
||||
&& !std::is_lvalue_reference_v<Value>>>
|
||||
auto wrap_moveable_as_copyable(Value &&value) {
|
||||
return moveable_as_copyable_wrap<Value>(std::move(value));
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
class unique_any;
|
||||
|
||||
template <typename Value>
|
||||
Value *any_cast(unique_any *value) noexcept;
|
||||
|
||||
template <typename Value>
|
||||
const Value *any_cast(const unique_any *value) noexcept;
|
||||
|
||||
class unique_any final {
|
||||
public:
|
||||
// Construction and destruction [any.cons]
|
||||
constexpr unique_any() noexcept {
|
||||
}
|
||||
|
||||
unique_any(const unique_any &other) = delete;
|
||||
unique_any &operator=(const unique_any &other) = delete;
|
||||
|
||||
unique_any(unique_any &&other) noexcept
|
||||
: _impl(std::move(other._impl)) {
|
||||
}
|
||||
|
||||
unique_any &operator=(unique_any &&other) noexcept {
|
||||
_impl = std::move(other._impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<Value>, unique_any>>>
|
||||
unique_any(Value &&other)
|
||||
: unique_any(
|
||||
std::forward<Value>(other),
|
||||
std::is_copy_constructible<std::decay_t<Value>>()) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<Value>, unique_any>>>
|
||||
unique_any &operator=(Value &&other) {
|
||||
if constexpr (std::is_copy_constructible_v<std::decay_t<Value>>) {
|
||||
_impl = std::forward<Value>(other);
|
||||
} else if constexpr (std::is_move_constructible_v<std::decay_t<Value>>
|
||||
&& !std::is_lvalue_reference_v<Value>) {
|
||||
_impl = details::wrap_moveable_as_copyable(std::move(other));
|
||||
} else {
|
||||
static_assert(
|
||||
false_t(Value{}),
|
||||
"Bad value for base::unique_any.");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<
|
||||
std::is_constructible_v<std::decay_t<Value>, Args...>
|
||||
&& std::is_copy_constructible_v<decay_t<Value>>>>
|
||||
std::decay_t<Value> &emplace(Args &&...args) {
|
||||
return _impl.emplace<Value>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void reset() noexcept {
|
||||
_impl.reset();
|
||||
}
|
||||
|
||||
void swap(unique_any &other) noexcept {
|
||||
_impl.swap(other._impl);
|
||||
}
|
||||
|
||||
bool has_value() const noexcept {
|
||||
return _impl.has_value();
|
||||
}
|
||||
|
||||
// Should check if it is a moveable_only wrap first.
|
||||
//const std::type_info &type() const noexcept {
|
||||
// return _impl.type();
|
||||
//}
|
||||
|
||||
private:
|
||||
template <
|
||||
typename Value,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<Value>, unique_any>
|
||||
&& std::is_copy_constructible_v<std::decay_t<Value>>>>
|
||||
unique_any(Value &&other, std::true_type)
|
||||
: _impl(std::forward<Value>(other)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<Value>, unique_any>
|
||||
&& !std::is_copy_constructible_v<std::decay_t<Value>>
|
||||
&& std::is_move_constructible_v<std::decay_t<Value>>
|
||||
&& !std::is_lvalue_reference_v<Value>>>
|
||||
unique_any(Value &&other, std::false_type)
|
||||
: _impl(details::wrap_moveable_as_copyable(std::move(other))) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename ...Args>
|
||||
friend unique_any make_any(Args &&...args);
|
||||
|
||||
template <typename Value>
|
||||
friend const Value *any_cast(const unique_any *value) noexcept;
|
||||
|
||||
template <typename Value>
|
||||
friend Value *any_cast(unique_any *value) noexcept;
|
||||
|
||||
std::any _impl;
|
||||
|
||||
};
|
||||
|
||||
inline void swap(unique_any &a, unique_any &b) noexcept {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename ...Args>
|
||||
inline auto make_any(Args &&...args)
|
||||
-> std::enable_if_t<
|
||||
std::is_copy_constructible_v<std::decay_t<Value>>,
|
||||
unique_any> {
|
||||
return std::make_any<Value>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename ...Args>
|
||||
inline auto make_any(Args &&...args)
|
||||
-> std::enable_if_t<
|
||||
!std::is_copy_constructible_v<std::decay_t<Value>>
|
||||
&& std::is_move_constructible_v<std::decay_t<Value>>,
|
||||
unique_any> {
|
||||
return Value(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
inline Value *any_cast(unique_any *value) noexcept {
|
||||
if constexpr (std::is_copy_constructible_v<Value>) {
|
||||
return std::any_cast<Value>(&value->_impl);
|
||||
} else if constexpr (std::is_move_constructible_v<Value>) {
|
||||
auto wrap = std::any_cast<
|
||||
details::moveable_as_copyable_wrap<Value>
|
||||
>(&value->_impl);
|
||||
return wrap ? &wrap->value : nullptr;
|
||||
} else {
|
||||
static_assert(
|
||||
false_t(Value{}),
|
||||
"Bad type for base::any_cast.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
inline const Value *any_cast(const unique_any *value) noexcept {
|
||||
if constexpr (std::is_copy_constructible_v<Value>) {
|
||||
return std::any_cast<Value>(&value->_impl);
|
||||
} else if constexpr (std::is_move_constructible_v<Value>) {
|
||||
auto wrap = std::any_cast<
|
||||
details::moveable_as_copyable_wrap<Value>
|
||||
>(&value->_impl);
|
||||
return wrap ? &wrap->value : nullptr;
|
||||
} else {
|
||||
static_assert(
|
||||
false_t(Value{}),
|
||||
"Bad type for base::any_cast.");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,178 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#ifndef Unexpected
|
||||
#define Unexpected(message) std::abort()
|
||||
#define UniqueFunctionUnexpected
|
||||
#endif // Unexpected
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
|
||||
template <typename Callable>
|
||||
class moveable_callable_wrap {
|
||||
public:
|
||||
static_assert(
|
||||
std::is_move_constructible_v<Callable>,
|
||||
"Should be at least moveable.");
|
||||
|
||||
moveable_callable_wrap(Callable &&other)
|
||||
: _value(std::move(other)) {
|
||||
}
|
||||
moveable_callable_wrap &operator=(Callable &&other) {
|
||||
_value = std::move(other);
|
||||
return *this;
|
||||
}
|
||||
moveable_callable_wrap(moveable_callable_wrap &&other)
|
||||
: _value(std::move(other._value)) {
|
||||
}
|
||||
moveable_callable_wrap(
|
||||
const moveable_callable_wrap &other)
|
||||
: _value(fail_construct()) {
|
||||
}
|
||||
moveable_callable_wrap &operator=(
|
||||
moveable_callable_wrap &&other) {
|
||||
_value = std::move(other._value);
|
||||
return *this;
|
||||
}
|
||||
moveable_callable_wrap &operator=(
|
||||
const moveable_callable_wrap &other) {
|
||||
return fail_assign();
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
decltype(auto) operator()(Args &&...args) {
|
||||
return _value(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
[[noreturn]] Callable fail_construct() {
|
||||
Unexpected("Attempt to copy-construct a move-only type.");
|
||||
}
|
||||
[[noreturn]] moveable_callable_wrap &fail_assign() {
|
||||
Unexpected("Attempt to copy-assign a move-only type.");
|
||||
}
|
||||
|
||||
Callable _value;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Function>
|
||||
class unique_function;
|
||||
|
||||
template <typename Return, typename ...Args>
|
||||
class unique_function<Return(Args...)> final {
|
||||
public:
|
||||
unique_function(std::nullptr_t = nullptr) noexcept {
|
||||
}
|
||||
unique_function(const unique_function &other) = delete;
|
||||
unique_function &operator=(const unique_function &other) = delete;
|
||||
|
||||
// Move construct / assign from the same type.
|
||||
unique_function(unique_function &&other)
|
||||
: _impl(std::move(other._impl)) {
|
||||
}
|
||||
unique_function &operator=(unique_function &&other) {
|
||||
_impl = std::move(other._impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Callable,
|
||||
typename = std::enable_if_t<
|
||||
std::is_convertible_v<
|
||||
decltype(std::declval<Callable>()(
|
||||
std::declval<Args>()...)),
|
||||
Return>>>
|
||||
unique_function(Callable &&other)
|
||||
: unique_function(
|
||||
std::forward<Callable>(other),
|
||||
std::is_copy_constructible<std::decay_t<Callable>>{}) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Callable,
|
||||
typename = std::enable_if_t<
|
||||
std::is_convertible_v<
|
||||
decltype(std::declval<Callable>()(
|
||||
std::declval<Args>()...)),
|
||||
Return>>>
|
||||
unique_function &operator=(Callable &&other) {
|
||||
using Decayed = std::decay_t<Callable>;
|
||||
if constexpr (std::is_copy_constructible_v<Decayed>) {
|
||||
_impl = std::forward<Callable>(other);
|
||||
} else if constexpr (std::is_move_constructible_v<Decayed>) {
|
||||
_impl = details::moveable_callable_wrap<Decayed>(
|
||||
std::forward<Callable>(other));
|
||||
} else {
|
||||
static_assert(false_t(other), "Should be moveable.");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(unique_function &other) {
|
||||
_impl.swap(other._impl);
|
||||
}
|
||||
|
||||
Return operator()(Args ...args) {
|
||||
return _impl(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return _impl.operator bool();
|
||||
}
|
||||
|
||||
friend inline bool operator==(
|
||||
const unique_function &value,
|
||||
std::nullptr_t) noexcept {
|
||||
return value._impl == nullptr;
|
||||
}
|
||||
friend inline bool operator==(
|
||||
std::nullptr_t,
|
||||
const unique_function &value) noexcept {
|
||||
return value._impl == nullptr;
|
||||
}
|
||||
friend inline bool operator!=(
|
||||
const unique_function &value,
|
||||
std::nullptr_t) noexcept {
|
||||
return value._impl != nullptr;
|
||||
}
|
||||
friend inline bool operator!=(
|
||||
std::nullptr_t,
|
||||
const unique_function &value) noexcept {
|
||||
return value._impl != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Callable>
|
||||
unique_function(Callable &&other, std::true_type)
|
||||
: _impl(std::forward<Callable>(other)) {
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
unique_function(Callable &&other, std::false_type)
|
||||
: _impl(details::moveable_callable_wrap<std::decay_t<Callable>>(
|
||||
std::forward<Callable>(other))) {
|
||||
}
|
||||
|
||||
std::function<Return(Args...)> _impl;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#ifdef UniqueFunctionUnexpected
|
||||
#undef UniqueFunctionUnexpected
|
||||
#undef Unexpected
|
||||
#endif // UniqueFunctionUnexpectedb
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename T>
|
||||
class unique_qptr {
|
||||
public:
|
||||
unique_qptr() = default;
|
||||
unique_qptr(std::nullptr_t) noexcept {
|
||||
}
|
||||
explicit unique_qptr(T *pointer) noexcept
|
||||
: _object(pointer) {
|
||||
}
|
||||
|
||||
unique_qptr(const unique_qptr &other) = delete;
|
||||
unique_qptr &operator=(const unique_qptr &other) = delete;
|
||||
unique_qptr(unique_qptr &&other) noexcept
|
||||
: _object(base::take(other._object)) {
|
||||
}
|
||||
unique_qptr &operator=(unique_qptr &&other) noexcept {
|
||||
if (_object != other._object) {
|
||||
destroy();
|
||||
_object = base::take(other._object);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename U,
|
||||
typename = std::enable_if_t<std::is_base_of_v<T, U>>>
|
||||
unique_qptr(unique_qptr<U> &&other) noexcept
|
||||
: _object(base::take(other._object)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename U,
|
||||
typename = std::enable_if_t<std::is_base_of_v<T, U>>>
|
||||
unique_qptr &operator=(unique_qptr<U> &&other) noexcept {
|
||||
if (_object != other._object) {
|
||||
destroy();
|
||||
_object = base::take(other._object);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
unique_qptr &operator=(std::nullptr_t) noexcept {
|
||||
destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
explicit unique_qptr(std::in_place_t, Args &&...args)
|
||||
: _object(new T(std::forward<Args>(args)...)) {
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
T *emplace(Args &&...args) {
|
||||
reset(new T(std::forward<Args>(args)...));
|
||||
return get();
|
||||
}
|
||||
|
||||
void reset(T *value = nullptr) noexcept {
|
||||
if (_object != value) {
|
||||
destroy();
|
||||
_object = value;
|
||||
}
|
||||
}
|
||||
|
||||
T *get() const noexcept {
|
||||
return static_cast<T*>(_object.data());
|
||||
}
|
||||
operator T*() const noexcept {
|
||||
return get();
|
||||
}
|
||||
|
||||
T *release() noexcept {
|
||||
return static_cast<T*>(base::take(_object).data());
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return _object != nullptr;
|
||||
}
|
||||
|
||||
T *operator->() const noexcept {
|
||||
return get();
|
||||
}
|
||||
T &operator*() const noexcept {
|
||||
return *get();
|
||||
}
|
||||
|
||||
~unique_qptr() noexcept {
|
||||
destroy();
|
||||
}
|
||||
|
||||
private:
|
||||
void destroy() noexcept {
|
||||
delete base::take(_object).data();
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
friend class unique_qptr;
|
||||
|
||||
QPointer<QObject> _object;
|
||||
|
||||
};
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
inline unique_qptr<T> make_unique_q(Args &&...args) {
|
||||
return unique_qptr<T>(std::in_place, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/unixtime.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#elif defined Q_OS_MAC
|
||||
#include <mach/mach_time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace unixtime {
|
||||
namespace {
|
||||
|
||||
std::atomic<bool> ValueUpdated/* = false*/;
|
||||
std::atomic<TimeId> ValueShift/* = 0*/;
|
||||
std::atomic<bool> HttpValueValid/* = false*/;
|
||||
std::atomic<TimeId> HttpValueShift/* = 0*/;
|
||||
|
||||
class MsgIdManager {
|
||||
public:
|
||||
MsgIdManager();
|
||||
|
||||
void update();
|
||||
[[nodiscard]] uint64 next();
|
||||
|
||||
private:
|
||||
void initialize();
|
||||
|
||||
QReadWriteLock _lock;
|
||||
uint64 _startId = 0;
|
||||
std::atomic<uint32> _incrementedPart = 0;
|
||||
uint64 _startCounter = 0;
|
||||
uint64 _randomPart = 0;
|
||||
float64 _multiplier = 0.;
|
||||
|
||||
};
|
||||
|
||||
MsgIdManager GlobalMsgIdManager;
|
||||
|
||||
[[nodiscard]] float64 GetMultiplier() {
|
||||
// 0xFFFF0000 instead of 0x100000000 to make msgId grow slightly slower,
|
||||
// than unixtime and we had time to reconfigure.
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceFrequency(&li);
|
||||
return float64(0xFFFF0000L) / float64(li.QuadPart);
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
mach_timebase_info_data_t tb = { 0, 0 };
|
||||
mach_timebase_info(&tb);
|
||||
const auto frequency = (float64(tb.numer) / tb.denom) / 1000000.;
|
||||
return frequency * (float64(0xFFFF0000L) / 1000.);
|
||||
#else // Q_OS_MAC || Q_OS_WIN
|
||||
return float64(0xFFFF0000L) / 1000000000.;
|
||||
#endif // Q_OS_MAC || Q_OS_WIN
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64 GetCounter() {
|
||||
#ifdef Q_OS_WIN
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
return li.QuadPart;
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
return mach_absolute_time();
|
||||
#else // Q_OS_MAC || Q_OS_WIN
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
|
||||
#endif // Q_OS_MAC || Q_OS_WIN
|
||||
}
|
||||
|
||||
MsgIdManager::MsgIdManager() {
|
||||
auto generator = std::mt19937(std::random_device()());
|
||||
auto distribution = std::uniform_int_distribution<uint32>();
|
||||
_randomPart = distribution(generator);
|
||||
_multiplier = GetMultiplier();
|
||||
initialize();
|
||||
|
||||
srand(uint32(_startCounter & 0xFFFFFFFFUL));
|
||||
}
|
||||
|
||||
void MsgIdManager::update() {
|
||||
QWriteLocker lock(&_lock);
|
||||
initialize();
|
||||
}
|
||||
|
||||
void MsgIdManager::initialize() {
|
||||
_startCounter = GetCounter();
|
||||
_startId = ((uint64(uint32(now()))) << 32) | _randomPart;
|
||||
}
|
||||
|
||||
uint64 MsgIdManager::next() {
|
||||
const auto counter = GetCounter();
|
||||
|
||||
QReadLocker lock(&_lock);
|
||||
const auto delta = (counter - _startCounter);
|
||||
const auto result = _startId + (uint64)floor(delta * _multiplier);
|
||||
lock.unlock();
|
||||
|
||||
return (result & ~0x03L) + (_incrementedPart += 4);
|
||||
}
|
||||
|
||||
TimeId local() {
|
||||
return (TimeId)time(nullptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TimeId now() {
|
||||
return local() + ValueShift.load();
|
||||
}
|
||||
|
||||
void update(TimeId now, bool force) {
|
||||
if (force) {
|
||||
ValueUpdated = true;
|
||||
} else {
|
||||
auto expected = false;
|
||||
if (!ValueUpdated.compare_exchange_strong(expected, true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto shift = now + 1 - local();
|
||||
ValueShift = shift;
|
||||
|
||||
HttpValueShift = 0;
|
||||
HttpValueValid = false;
|
||||
|
||||
GlobalMsgIdManager.update();
|
||||
}
|
||||
|
||||
QDateTime parse(TimeId value) {
|
||||
return (value > 0)
|
||||
? QDateTime::fromTime_t(value - ValueShift)
|
||||
: QDateTime();
|
||||
}
|
||||
|
||||
TimeId serialize(const QDateTime &date) {
|
||||
return date.isNull() ? TimeId(0) : date.toTime_t() + ValueShift;
|
||||
}
|
||||
|
||||
bool http_valid() {
|
||||
return HttpValueValid;
|
||||
}
|
||||
|
||||
TimeId http_now() {
|
||||
return now() + HttpValueShift;
|
||||
}
|
||||
|
||||
void http_update(TimeId now) {
|
||||
HttpValueShift = now - base::unixtime::now();
|
||||
HttpValueValid = true;
|
||||
}
|
||||
|
||||
void http_invalidate() {
|
||||
HttpValueValid = false;
|
||||
}
|
||||
|
||||
uint64 mtproto_msg_id() {
|
||||
return GlobalMsgIdManager.next();
|
||||
}
|
||||
|
||||
} // namespace unixtime
|
||||
} // namespace base
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
|
||||
class QDateTime;
|
||||
|
||||
namespace base {
|
||||
namespace unixtime {
|
||||
|
||||
// All functions are thread-safe.
|
||||
|
||||
[[nodiscard]] TimeId now();
|
||||
void update(TimeId now, bool force = false);
|
||||
|
||||
[[nodiscard]] QDateTime parse(TimeId value);
|
||||
[[nodiscard]] TimeId serialize(const QDateTime &date);
|
||||
|
||||
[[nodiscard]] bool http_valid();
|
||||
[[nodiscard]] TimeId http_now();
|
||||
void http_update(TimeId now);
|
||||
void http_invalidate();
|
||||
|
||||
[[nodiscard]] uint64 mtproto_msg_id();
|
||||
|
||||
} // namespace unixtime
|
||||
} // namespace base
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace base {
|
||||
namespace details {
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
typename Operator,
|
||||
typename = decltype(Operator{}(
|
||||
std::declval<Type>(),
|
||||
std::declval<Type>()))>
|
||||
char test_operator(const Type &, const Operator &);
|
||||
int test_operator(...);
|
||||
|
||||
template <typename Type, template <typename> typename Operator>
|
||||
struct has_operator
|
||||
: std::bool_constant<
|
||||
sizeof(test_operator(
|
||||
std::declval<Type>(),
|
||||
std::declval<Operator<Type>>()
|
||||
)) == sizeof(char)> {
|
||||
};
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
template <typename> typename Operator>
|
||||
constexpr bool has_operator_v
|
||||
= has_operator<Type, Operator>::value;
|
||||
|
||||
template <typename Type>
|
||||
constexpr bool has_less_v = has_operator_v<Type, std::less>;
|
||||
|
||||
template <typename Type>
|
||||
constexpr bool has_greater_v = has_operator_v<Type, std::greater>;
|
||||
|
||||
template <typename Type>
|
||||
constexpr bool has_less_equal_v = has_operator_v<Type, std::less_equal>;
|
||||
|
||||
template <typename Type>
|
||||
constexpr bool has_greater_equal_v = has_operator_v<Type, std::greater_equal>;
|
||||
|
||||
template <typename Type>
|
||||
constexpr bool has_equal_to_v = has_operator_v<Type, std::equal_to>;
|
||||
|
||||
template <typename Type>
|
||||
constexpr bool has_not_equal_to_v = has_operator_v<Type, std::not_equal_to>;
|
||||
|
||||
} // namespace details
|
||||
} // namespace base
|
||||
|
||||
template <
|
||||
typename ValueType,
|
||||
typename Helper = decltype(
|
||||
value_ordering_helper(std::declval<ValueType>()))>
|
||||
inline auto operator<(const ValueType &a, const ValueType &b)
|
||||
-> std::enable_if_t<base::details::has_less_v<Helper>, bool> {
|
||||
return value_ordering_helper(a) < value_ordering_helper(b);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ValueType,
|
||||
typename Helper = decltype(
|
||||
value_ordering_helper(std::declval<ValueType>()))>
|
||||
inline auto operator>(const ValueType &a, const ValueType &b)
|
||||
-> std::enable_if_t<
|
||||
base::details::has_greater_v<Helper>
|
||||
|| base::details::has_less_v<Helper>,
|
||||
bool
|
||||
> {
|
||||
if constexpr (base::details::has_greater_v<Helper>) {
|
||||
return value_ordering_helper(a) > value_ordering_helper(b);
|
||||
} else {
|
||||
return value_ordering_helper(b) < value_ordering_helper(a);
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename ValueType,
|
||||
typename Helper = decltype(
|
||||
value_ordering_helper(std::declval<ValueType>()))>
|
||||
inline auto operator<=(const ValueType &a, const ValueType &b)
|
||||
-> std::enable_if_t<
|
||||
base::details::has_less_equal_v<Helper>
|
||||
|| base::details::has_less_v<Helper>,
|
||||
bool
|
||||
> {
|
||||
if constexpr (base::details::has_less_equal_v<Helper>) {
|
||||
return value_ordering_helper(a) <= value_ordering_helper(b);
|
||||
} else {
|
||||
return !(value_ordering_helper(b) < value_ordering_helper(a));
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename ValueType,
|
||||
typename Helper = decltype(
|
||||
value_ordering_helper(std::declval<ValueType>()))>
|
||||
inline auto operator>=(const ValueType &a, const ValueType &b)
|
||||
-> std::enable_if_t<
|
||||
base::details::has_greater_equal_v<Helper>
|
||||
|| base::details::has_less_v<Helper>,
|
||||
bool
|
||||
> {
|
||||
if constexpr (base::details::has_greater_equal_v<Helper>) {
|
||||
return value_ordering_helper(a) >= value_ordering_helper(b);
|
||||
} else {
|
||||
return !(value_ordering_helper(a) < value_ordering_helper(b));
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename ValueType,
|
||||
typename Helper = decltype(
|
||||
value_ordering_helper(std::declval<ValueType>()))>
|
||||
inline auto operator==(const ValueType &a, const ValueType &b)
|
||||
-> std::enable_if_t<base::details::has_equal_to_v<Helper>, bool> {
|
||||
return value_ordering_helper(a) == value_ordering_helper(b);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ValueType,
|
||||
typename Helper = decltype(
|
||||
value_ordering_helper(std::declval<ValueType>()))>
|
||||
inline auto operator!=(const ValueType &a, const ValueType &b)
|
||||
-> std::enable_if_t<
|
||||
base::details::has_not_equal_to_v<Helper>
|
||||
|| base::details::has_equal_to_v<Helper>,
|
||||
bool
|
||||
> {
|
||||
if constexpr (base::details::has_not_equal_to_v<Helper>) {
|
||||
return value_ordering_helper(a) != value_ordering_helper(b);
|
||||
} else {
|
||||
return !(value_ordering_helper(a) == value_ordering_helper(b));
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
inline bool operator<(std::nullopt_t, std::nullopt_t) {
|
||||
return false;
|
||||
}
|
||||
inline bool operator>(std::nullopt_t, std::nullopt_t) {
|
||||
return false;
|
||||
}
|
||||
inline bool operator<=(std::nullopt_t, std::nullopt_t) {
|
||||
return true;
|
||||
}
|
||||
inline bool operator>=(std::nullopt_t, std::nullopt_t) {
|
||||
return true;
|
||||
}
|
||||
inline bool operator==(std::nullopt_t, std::nullopt_t) {
|
||||
return true;
|
||||
}
|
||||
inline bool operator!=(std::nullopt_t, std::nullopt_t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#include <mapbox/variant.hpp>
|
||||
#include <rpl/details/type_list.h>
|
||||
#include "base/match_method.h"
|
||||
#include "base/assertion.h"
|
||||
|
||||
// We use base::variant<> alias and base::get_if() helper while we don't have std::variant<>.
|
||||
namespace base {
|
||||
|
||||
template <typename... Types>
|
||||
using variant = mapbox::util::variant<Types...>;
|
||||
|
||||
template <typename T, typename... Types>
|
||||
inline T *get_if(variant<Types...> *v) {
|
||||
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
|
||||
}
|
||||
|
||||
template <typename T, typename... Types>
|
||||
inline const T *get_if(const variant<Types...> *v) {
|
||||
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
|
||||
}
|
||||
|
||||
namespace type_list = rpl::details::type_list;
|
||||
|
||||
template <typename ...Types>
|
||||
struct normalized_variant {
|
||||
using list = type_list::list<Types...>;
|
||||
using distinct = type_list::distinct_t<list>;
|
||||
using type = std::conditional_t<
|
||||
type_list::size_v<distinct> == 1,
|
||||
type_list::get_t<0, distinct>,
|
||||
type_list::extract_to_t<distinct, base::variant>>;
|
||||
};
|
||||
|
||||
template <typename ...Types>
|
||||
using normalized_variant_t
|
||||
= typename normalized_variant<Types...>::type;
|
||||
|
||||
template <typename TypeList, typename Variant, typename ...Methods>
|
||||
struct match_helper;
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
typename ...Types,
|
||||
typename Variant,
|
||||
typename ...Methods>
|
||||
struct match_helper<type_list::list<Type, Types...>, Variant, Methods...> {
|
||||
static decltype(auto) call(Variant &value, Methods &&...methods) {
|
||||
if (const auto v = get_if<Type>(&value)) {
|
||||
return match_method(
|
||||
*v,
|
||||
std::forward<Methods>(methods)...);
|
||||
}
|
||||
return match_helper<
|
||||
type_list::list<Types...>,
|
||||
Variant,
|
||||
Methods...>::call(
|
||||
value,
|
||||
std::forward<Methods>(methods)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
typename Variant,
|
||||
typename ...Methods>
|
||||
struct match_helper<type_list::list<Type>, Variant, Methods...> {
|
||||
static decltype(auto) call(Variant &value, Methods &&...methods) {
|
||||
if (const auto v = get_if<Type>(&value)) {
|
||||
return match_method(
|
||||
*v,
|
||||
std::forward<Methods>(methods)...);
|
||||
}
|
||||
Unexpected("Valueless variant in base::match().");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ...Types, typename ...Methods>
|
||||
inline decltype(auto) match(
|
||||
variant<Types...> &value,
|
||||
Methods &&...methods) {
|
||||
return match_helper<
|
||||
type_list::list<Types...>,
|
||||
variant<Types...>,
|
||||
Methods...>::call(value, std::forward<Methods>(methods)...);
|
||||
}
|
||||
|
||||
template <typename ...Types, typename ...Methods>
|
||||
inline decltype(auto) match(
|
||||
const variant<Types...> &value,
|
||||
Methods &&...methods) {
|
||||
return match_helper<
|
||||
type_list::list<Types...>,
|
||||
const variant<Types...>,
|
||||
Methods...>::call(value, std::forward<Methods>(methods)...);
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,704 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Object, typename ParentObject = void>
|
||||
class virtual_object;
|
||||
|
||||
template <typename ConcreteMethod, typename ReturnType, typename ...Args>
|
||||
class virtual_method;
|
||||
|
||||
template <typename ConcreteMethod, typename BaseMethod>
|
||||
class virtual_override;
|
||||
|
||||
namespace virtual_methods {
|
||||
|
||||
struct child_entry;
|
||||
using is_parent_check = bool(*)(const child_entry &possible_parent);
|
||||
struct child_entry {
|
||||
is_parent_check check_is_parent;
|
||||
int *table_index;
|
||||
};
|
||||
using child_entries = std::vector<child_entry>;
|
||||
|
||||
// Recursive method to find if some class is a child of some other class.
|
||||
template <typename ConcreteObject>
|
||||
struct is_parent {
|
||||
static inline bool check(const child_entry &possible_parent) {
|
||||
// Generate a good error message if ConcreteObject is not a child of virtual_object<>.
|
||||
using all_objects_must_derive_virtual_object = typename ConcreteObject::virtual_object_parent;
|
||||
using ConcreteObjectParent = all_objects_must_derive_virtual_object;
|
||||
return (possible_parent.check_is_parent == &is_parent<ConcreteObject>::check)
|
||||
|| is_parent<ConcreteObjectParent>::check(possible_parent);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_parent<void> {
|
||||
static inline bool check(const child_entry &possible_parent) {
|
||||
return (possible_parent.check_is_parent == &is_parent<void>::check);
|
||||
}
|
||||
};
|
||||
|
||||
// Just force the compiler not to optimize away the object that "enforce" points at.
|
||||
inline void dont_optimize_away(void *enforce) {
|
||||
static volatile void *result = nullptr;
|
||||
if (result) {
|
||||
result = enforce;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type, Type Value>
|
||||
struct dont_optimize_away_struct {
|
||||
};
|
||||
|
||||
inline bool first_dispatch_fired(bool did_fire = false) {
|
||||
static bool fired = false;
|
||||
if (did_fire) {
|
||||
fired = true;
|
||||
}
|
||||
return fired;
|
||||
}
|
||||
|
||||
template <typename Object, void (*Creator)(const child_entry &)>
|
||||
class object_registrator {
|
||||
public:
|
||||
inline object_registrator() {
|
||||
Assert(!first_dispatch_fired());
|
||||
Creator(child_entry {
|
||||
&is_parent<Object>::check,
|
||||
&_index,
|
||||
});
|
||||
}
|
||||
static inline int &Index() {
|
||||
return _index;
|
||||
}
|
||||
|
||||
private:
|
||||
static int _index;
|
||||
|
||||
};
|
||||
|
||||
template <typename Object, void (*Creator)(const child_entry &)>
|
||||
int object_registrator<Object, Creator>::_index = -1;
|
||||
|
||||
class object_base {
|
||||
protected:
|
||||
virtual ~object_base() = default;
|
||||
|
||||
};
|
||||
|
||||
template <typename ...ConcreteArgs>
|
||||
struct multi_index_collector;
|
||||
template <int M, typename ...ConcreteArgs>
|
||||
struct override_key_collector_helper;
|
||||
template <typename Call, typename ...Args>
|
||||
struct table_fill_entry_helper;
|
||||
template <typename ...Args>
|
||||
struct table_count_size;
|
||||
|
||||
} // namespace virtual_methods
|
||||
|
||||
// This should be a base class for every child object in your hierarchy.
|
||||
// It registers this child in the root virtual_objects classes list.
|
||||
// Also it holds its own index in the classes list that is used for fast
|
||||
// invoking of methods from the virtual tables in different virtual_methods.
|
||||
template <typename Object, typename ParentObject>
|
||||
class virtual_object : public ParentObject {
|
||||
protected:
|
||||
virtual ~virtual_object() {
|
||||
virtual_methods::dont_optimize_away(&_virtual_object_registrator);
|
||||
}
|
||||
|
||||
private:
|
||||
using virtual_object_parent = ParentObject;
|
||||
|
||||
friend struct virtual_methods::is_parent<Object>;
|
||||
template <typename ...Args>
|
||||
friend struct virtual_methods::multi_index_collector;
|
||||
template <int M, typename ...ConcreteArgs>
|
||||
friend struct virtual_methods::override_key_collector_helper;
|
||||
template <typename OtherObject, typename OtherParentObject>
|
||||
friend class virtual_object;
|
||||
template <typename BaseMethod, typename ReturnType, typename ...Args>
|
||||
friend class virtual_method;
|
||||
|
||||
static inline void virtual_object_register_child(const virtual_methods::child_entry &entry) {
|
||||
return ParentObject::virtual_object_register_child(entry);
|
||||
}
|
||||
|
||||
using virtual_object_registrator = virtual_methods::object_registrator<Object, &virtual_object::virtual_object_register_child>;
|
||||
static virtual_object_registrator _virtual_object_registrator;
|
||||
using virtual_object_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct<virtual_object_registrator*, &_virtual_object_registrator>;
|
||||
|
||||
static inline int &virtual_object_child_index_static() {
|
||||
return virtual_object_registrator::Index();
|
||||
}
|
||||
int &virtual_object_child_index() override {
|
||||
return virtual_object_child_index_static();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Object, typename ParentObject>
|
||||
typename virtual_object<Object, ParentObject>::virtual_object_registrator virtual_object<Object, ParentObject>::_virtual_object_registrator = {};
|
||||
|
||||
// This should be a base class for the root of the whole hierarchy.
|
||||
// It holds the table of all child classes in a list.
|
||||
// This list is used by virtual_methods to generate virtual table.
|
||||
template <typename Object>
|
||||
class virtual_object<Object, void> : public virtual_methods::object_base {
|
||||
protected:
|
||||
virtual ~virtual_object() {
|
||||
virtual_methods::dont_optimize_away(&_virtual_object_registrator);
|
||||
}
|
||||
|
||||
private:
|
||||
using virtual_object_parent = void;
|
||||
|
||||
friend struct virtual_methods::is_parent<Object>;
|
||||
template <typename ...Args>
|
||||
friend struct virtual_methods::table_count_size;
|
||||
template <typename ...Args>
|
||||
friend struct virtual_methods::multi_index_collector;
|
||||
template <int M, typename ...ConcreteArgs>
|
||||
friend struct virtual_methods::override_key_collector_helper;
|
||||
template <typename Call, typename ...Args>
|
||||
friend struct virtual_methods::table_fill_entry_helper;
|
||||
template <typename OtherObject, typename OtherParentObject>
|
||||
friend class virtual_object;
|
||||
template <typename BaseMethod, typename ReturnType, typename ...Args>
|
||||
friend class virtual_method;
|
||||
|
||||
static inline virtual_methods::child_entries &virtual_object_get_child_entries() {
|
||||
static virtual_methods::child_entries entries;
|
||||
return entries;
|
||||
}
|
||||
|
||||
// Registers a new child class.
|
||||
// After that on the next call to virtual_method::virtual_method_prepare_table() will
|
||||
// generate a new virtual table for that virtual method.
|
||||
static inline void virtual_object_register_child(const virtual_methods::child_entry &entry) {
|
||||
auto &entries = virtual_object_get_child_entries();
|
||||
for (auto i = entries.begin(), e = entries.end(); i != e; ++i) {
|
||||
if (entry.check_is_parent(*i)) {
|
||||
*entry.table_index = (i - entries.begin());
|
||||
i = entries.insert(i, entry);
|
||||
for (++i, e = entries.end(); i != e; ++i) {
|
||||
++*(i->table_index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
*entry.table_index = entries.size();
|
||||
entries.push_back(entry);
|
||||
}
|
||||
|
||||
using virtual_object_registrator = virtual_methods::object_registrator<Object, &virtual_object::virtual_object_register_child>;
|
||||
static virtual_object_registrator _virtual_object_registrator;
|
||||
using virtual_object_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct<virtual_object_registrator*, &_virtual_object_registrator>;
|
||||
|
||||
static inline int &virtual_object_child_index_static() {
|
||||
return virtual_object_registrator::Index();
|
||||
}
|
||||
virtual int &virtual_object_child_index() {
|
||||
return virtual_object_child_index_static();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Object>
|
||||
typename virtual_object<Object, void>::virtual_object_registrator virtual_object<Object, void>::_virtual_object_registrator = {};
|
||||
|
||||
namespace virtual_methods {
|
||||
|
||||
template <typename Arg>
|
||||
struct is_virtual_argument : public std::integral_constant<bool,
|
||||
base::type_traits<Arg>::is_pointer::value
|
||||
? std::is_base_of<object_base, typename base::type_traits<Arg>::pointed_type>::value
|
||||
: false> {
|
||||
};
|
||||
|
||||
template <int N, int Instance>
|
||||
class multi_int_wrap {
|
||||
public:
|
||||
inline multi_int_wrap(int *indices) : _indices(indices) {
|
||||
}
|
||||
inline multi_int_wrap<N - 1, Instance> subindex() const {
|
||||
static_assert(N > 0, "Wrong multi_int_wrap created!");
|
||||
return multi_int_wrap<N - 1, Instance>(_indices + 1);
|
||||
}
|
||||
inline int ¤t() const {
|
||||
return *_indices;
|
||||
}
|
||||
|
||||
private:
|
||||
int *_indices;
|
||||
|
||||
};
|
||||
|
||||
template <int Instance>
|
||||
class multi_int_wrap<0, Instance> {
|
||||
public:
|
||||
inline multi_int_wrap(int *indices) {
|
||||
}
|
||||
inline int current() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <int N>
|
||||
using multi_index_wrap = multi_int_wrap<N, 0>;
|
||||
template <int N>
|
||||
using multi_size_wrap = multi_int_wrap<N, 1>;
|
||||
|
||||
template <typename ConcreteArg, typename ...ConcreteArgs>
|
||||
struct multi_index_collector<ConcreteArg, ConcreteArgs...> {
|
||||
static constexpr int N = sizeof...(ConcreteArgs) + 1;
|
||||
static inline void call(multi_index_wrap<N> indices, ConcreteArg arg, ConcreteArgs... args) {
|
||||
indices.current() = computeIndex(is_virtual_argument<ConcreteArg>(), arg);
|
||||
multi_index_collector<ConcreteArgs...>::call(indices.subindex(), args...);
|
||||
}
|
||||
|
||||
static inline int computeIndex(std::integral_constant<bool, false>, ConcreteArg arg) {
|
||||
return 0;
|
||||
}
|
||||
static inline int computeIndex(std::integral_constant<bool, true>, ConcreteArg arg) {
|
||||
return arg->virtual_object_child_index();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
struct multi_index_collector<> {
|
||||
static inline void call(multi_index_wrap<0> indices) {
|
||||
}
|
||||
};
|
||||
|
||||
template <int N>
|
||||
class override_key;
|
||||
|
||||
template <int N, int Instance>
|
||||
class multi_int {
|
||||
public:
|
||||
inline multi_int_wrap<N, Instance> data_wrap() {
|
||||
return multi_int_wrap<N, Instance>(_indices);
|
||||
}
|
||||
|
||||
template <typename ...ConcreteArgs>
|
||||
static inline multi_int<N, Instance> collect(ConcreteArgs... args) {
|
||||
multi_int<N, Instance> result;
|
||||
multi_index_collector<ConcreteArgs...>::call(result.data_wrap(), args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void reset() {
|
||||
memset(_indices, 0, sizeof(_indices));
|
||||
}
|
||||
|
||||
inline int value(int index) const {
|
||||
return _indices[index];
|
||||
}
|
||||
|
||||
inline void copy(multi_int_wrap<N, Instance> other) {
|
||||
memcpy(_indices, &other.current(), sizeof(_indices));
|
||||
}
|
||||
|
||||
private:
|
||||
int _indices[N] = { 0 };
|
||||
friend class override_key<N>;
|
||||
|
||||
};
|
||||
|
||||
template <int N>
|
||||
using multi_index = multi_int<N, 0>;
|
||||
template <int N>
|
||||
using multi_size = multi_int<N, 1>;
|
||||
|
||||
template <typename Call, int N>
|
||||
class table_data_wrap {
|
||||
public:
|
||||
inline table_data_wrap(Call *data, multi_size_wrap<N> size) : _data(data), _size(size) {
|
||||
}
|
||||
inline table_data_wrap<Call, N - 1> operator[](int index) const {
|
||||
return table_data_wrap<Call, N - 1>(_data + index * _size.subindex().current(), _size.subindex());
|
||||
}
|
||||
inline Call &operator[](multi_index_wrap<N> index) const {
|
||||
return (*this)[index.current()][index.subindex()];
|
||||
}
|
||||
inline int size() const {
|
||||
return count_size(std::integral_constant<int,N>());
|
||||
}
|
||||
|
||||
private:
|
||||
template <int M>
|
||||
inline int count_size(std::integral_constant<int,M>) const {
|
||||
return _size.current() / _size.subindex().current();
|
||||
}
|
||||
inline int count_size(std::integral_constant<int,1>) const {
|
||||
return _size.current();
|
||||
}
|
||||
|
||||
Call *_data;
|
||||
multi_size_wrap<N> _size;
|
||||
|
||||
};
|
||||
|
||||
template <typename Call>
|
||||
class table_data_wrap<Call, 0> {
|
||||
public:
|
||||
inline table_data_wrap(Call *data, multi_size_wrap<0> size) : _data(data) {
|
||||
}
|
||||
inline Call &operator[](multi_index_wrap<0> index) const {
|
||||
return *_data;
|
||||
}
|
||||
|
||||
private:
|
||||
Call *_data;
|
||||
|
||||
};
|
||||
|
||||
template <typename Call, int N>
|
||||
class table_data_wrap;
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
struct table_count_size<Arg, Args...> {
|
||||
static constexpr int N = sizeof...(Args) + 1;
|
||||
static inline void call(multi_size_wrap<N> index) {
|
||||
auto subindex = index.subindex();
|
||||
table_count_size<Args...>::call(subindex);
|
||||
index.current() = count(is_virtual_argument<Arg>()) * subindex.current();
|
||||
}
|
||||
|
||||
static inline int count(std::integral_constant<bool, false>) {
|
||||
return 1;
|
||||
}
|
||||
static inline int count(std::integral_constant<bool, true>) {
|
||||
return base::type_traits<Arg>::pointed_type::virtual_object_get_child_entries().size();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
struct table_count_size<> {
|
||||
static inline void call(multi_size_wrap<0> index) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Call, int N>
|
||||
class table_data {
|
||||
public:
|
||||
inline table_data_wrap<Call, N> data_wrap() {
|
||||
return table_data_wrap<Call, N>(_data.data(), _size.data_wrap());
|
||||
}
|
||||
|
||||
inline Call &operator[](multi_index<N> index) {
|
||||
int flat_index = 0;
|
||||
for (int i = 0; i != N - 1; ++i) {
|
||||
flat_index += _size.value(i + 1) * index.value(i);
|
||||
}
|
||||
flat_index += index.value(N - 1);
|
||||
return _data[flat_index];
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
inline bool changed() {
|
||||
if (!_data.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
multi_size<N> size;
|
||||
table_count_size<Args...>::call(size.data_wrap());
|
||||
_size = size;
|
||||
_data.resize(_size.value(0), nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Call> _data;
|
||||
multi_size<N> _size;
|
||||
|
||||
};
|
||||
|
||||
template <typename Call>
|
||||
class table_data<Call, 0> {
|
||||
public:
|
||||
inline table_data_wrap<Call, 0> data_wrap() {
|
||||
return table_data_wrap<Call, 0>(&_call, multi_size_wrap<0>(nullptr));
|
||||
}
|
||||
|
||||
inline Call &operator[](multi_index<0> index) {
|
||||
return _call;
|
||||
}
|
||||
|
||||
inline bool changed() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
Call _call = nullptr;
|
||||
|
||||
};
|
||||
|
||||
template <typename Call, typename ...Args>
|
||||
struct table_fill_entry_helper;
|
||||
|
||||
template <typename Call, typename Arg, typename ...Args>
|
||||
struct table_fill_entry_helper<Call, Arg, Args...> {
|
||||
static constexpr int N = sizeof...(Args) + 1;
|
||||
|
||||
static inline bool call(table_data_wrap<Call, N> table, multi_index_wrap<N> index, Call &fill) {
|
||||
auto start = index.current();
|
||||
for (auto i = start, count = table.size(); i != count; ++i) {
|
||||
auto foundGoodType = good(is_virtual_argument<Arg>(), start, index.current());
|
||||
if (foundGoodType) {
|
||||
index.current() = i;
|
||||
if (table_fill_entry_helper<Call, Args...>::call(table[i], index.subindex(), fill)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
index.current() = start;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool good(std::integral_constant<bool,false>, int start, int current) {
|
||||
return (start == current);
|
||||
}
|
||||
static inline bool good(std::integral_constant<bool,true>, int start, int current) {
|
||||
using BaseObject = typename base::type_traits<Arg>::pointed_type;
|
||||
auto &entries = BaseObject::virtual_object_get_child_entries();
|
||||
return (start == current) || entries[start].check_is_parent(entries[current]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Call>
|
||||
struct table_fill_entry_helper<Call> {
|
||||
static inline bool call(table_data_wrap<Call, 0> table, multi_index_wrap<0> index, Call &fill) {
|
||||
if (auto overrideMethod = table[index]) {
|
||||
fill = overrideMethod;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Call, int N>
|
||||
struct table_fill_entry;
|
||||
|
||||
template <typename ReturnType, int N, typename BaseMethod, typename ...Args>
|
||||
struct table_fill_entry<ReturnType(*)(BaseMethod*, Args...), N> {
|
||||
using Call = ReturnType(*)(BaseMethod*, Args...);
|
||||
static inline void call(table_data_wrap<Call, N> table, multi_index_wrap<N> index, Call &fill) {
|
||||
table_fill_entry_helper<Call, Args...>::call(table, index, fill);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Call, int N>
|
||||
inline void fill_entry(table_data_wrap<Call, N> table, multi_index_wrap<N> index, Call &fill) {
|
||||
return virtual_methods::table_fill_entry<Call, N>::call(table, index, fill);
|
||||
}
|
||||
|
||||
template <int M, typename ...ConcreteArgs>
|
||||
struct override_key_collector_helper;
|
||||
|
||||
template <int M, typename ConcreteArg, typename ...ConcreteArgs>
|
||||
struct override_key_collector_helper<M, ConcreteArg, ConcreteArgs...> {
|
||||
static inline void call(int **indices) {
|
||||
setValue(is_virtual_argument<ConcreteArg>(), indices);
|
||||
override_key_collector_helper<M + 1, ConcreteArgs...>::call(indices);
|
||||
}
|
||||
|
||||
static inline void setValue(std::integral_constant<bool,false>, int **indices) {
|
||||
indices[M] = nullptr;
|
||||
}
|
||||
static inline void setValue(std::integral_constant<bool,true>, int **indices) {
|
||||
using ConcreteObject = typename base::type_traits<ConcreteArg>::pointed_type;
|
||||
using IsParentCheckStruct = is_parent<ConcreteObject>;
|
||||
using IsParentCheckPointer = decltype(&IsParentCheckStruct::check);
|
||||
using override_key_collector_dont_optimize_away = dont_optimize_away_struct<IsParentCheckPointer, &IsParentCheckStruct::check>;
|
||||
override_key_collector_dont_optimize_away dont_optimize_away_object;
|
||||
(void)dont_optimize_away_object;
|
||||
|
||||
// Check that is_parent<> can be instantiated.
|
||||
// So every ConcreteObject is a valid child of virtual_object<>.
|
||||
dont_optimize_away(reinterpret_cast<void*>(&IsParentCheckStruct::check));
|
||||
indices[M] = &ConcreteObject::virtual_object_child_index_static();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <int M>
|
||||
struct override_key_collector_helper<M> {
|
||||
static inline void call(int **indices) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename CallSignature>
|
||||
struct override_key_collector;
|
||||
|
||||
template <typename ReturnType, typename BaseMethod, typename ...ConcreteArgs>
|
||||
struct override_key_collector<ReturnType(*)(BaseMethod, ConcreteArgs...)> {
|
||||
static inline void call(int **indices) {
|
||||
override_key_collector_helper<0, ConcreteArgs...>::call(indices);
|
||||
}
|
||||
};
|
||||
|
||||
template <int N>
|
||||
class override_key {
|
||||
public:
|
||||
inline multi_index<N> value() const {
|
||||
multi_index<N> result;
|
||||
for (int i = 0; i != N; ++i) {
|
||||
auto pointer = _indices[i];
|
||||
result._indices[i] = (pointer ? *pointer : 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
friend inline bool operator<(const override_key &k1, const override_key &k2) {
|
||||
for (int i = 0; i != N; ++i) {
|
||||
auto pointer1 = k1._indices[i], pointer2 = k2._indices[i];
|
||||
if (pointer1 < pointer2) {
|
||||
return true;
|
||||
} else if (pointer1 > pointer2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename CallSignature>
|
||||
inline void collect() {
|
||||
override_key_collector<CallSignature>::call(_indices);
|
||||
}
|
||||
|
||||
private:
|
||||
int *_indices[N];
|
||||
|
||||
};
|
||||
|
||||
template <typename BaseMethod, typename ConcreteMethod, typename CallSignature, typename ...Args>
|
||||
struct static_cast_helper;
|
||||
|
||||
template <typename BaseMethod, typename ConcreteMethod, typename ReturnType, typename ...ConcreteArgs, typename ...Args>
|
||||
struct static_cast_helper<BaseMethod, ConcreteMethod, ReturnType(*)(BaseMethod *, ConcreteArgs...), Args...> {
|
||||
static inline ReturnType call(BaseMethod *context, Args ...args) {
|
||||
return ConcreteMethod::call(context, static_cast<ConcreteArgs>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace virtual_methods
|
||||
|
||||
// This is a base class for all your virtual methods.
|
||||
// It dispatches a call to one of the registered virtual_overrides
|
||||
// or calls the fallback method of the BaseMethod class.
|
||||
template <typename BaseMethod, typename ReturnType, typename ...Args>
|
||||
class virtual_method {
|
||||
static constexpr int N = sizeof...(Args);
|
||||
using virtual_method_call = ReturnType(*)(BaseMethod *context, Args... args);
|
||||
|
||||
public:
|
||||
inline ReturnType call(Args... args) {
|
||||
auto context = static_cast<BaseMethod*>(this);
|
||||
auto index = virtual_methods::multi_index<N>::collect(args...);
|
||||
auto &table = virtual_method_prepare_table();
|
||||
auto &entry = table[index];
|
||||
if (!entry) {
|
||||
virtual_methods::fill_entry(table.data_wrap(), index.data_wrap(), entry);
|
||||
if (!entry) {
|
||||
entry = &virtual_method::virtual_method_base_instance;
|
||||
}
|
||||
}
|
||||
return (*entry)(context, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
// This map of methods contains only the original registered overrides.
|
||||
using virtual_method_override_key = virtual_methods::override_key<N>;
|
||||
using virtual_method_override_map = std::map<virtual_method_override_key, virtual_method_call>;
|
||||
static inline virtual_method_override_map &virtual_method_get_override_map() {
|
||||
static virtual_method_override_map override_map;
|
||||
return override_map;
|
||||
}
|
||||
|
||||
// This method generates and returns a virtual table which holds a method
|
||||
// for any child in the hierarchy or nullptr if none of the virtual_overrides fit.
|
||||
using virtual_method_table_data = virtual_methods::table_data<virtual_method_call, N>;
|
||||
static inline virtual_method_table_data &virtual_method_get_table_data() {
|
||||
static virtual_method_table_data virtual_table;
|
||||
return virtual_table;
|
||||
}
|
||||
|
||||
static inline virtual_method_table_data &virtual_method_prepare_table() {
|
||||
auto &virtual_table = virtual_method_get_table_data();
|
||||
if (virtual_table.template changed<Args...>()) {
|
||||
virtual_methods::first_dispatch_fired(true);
|
||||
|
||||
// The class hierarchy has changed - we need to generate the virtual table once again.
|
||||
// All other handlers will be placed if they're called.
|
||||
for (auto &i : virtual_method_get_override_map()) {
|
||||
virtual_table[i.first.value()] = i.second;
|
||||
}
|
||||
}
|
||||
return virtual_table;
|
||||
}
|
||||
|
||||
static ReturnType virtual_method_base_instance(BaseMethod *context, Args... args) {
|
||||
return BaseMethod::default_call(context, args...);
|
||||
}
|
||||
|
||||
template <typename ConcreteMethod>
|
||||
static ReturnType virtual_method_override_instance(BaseMethod *context, Args... args) {
|
||||
return virtual_methods::static_cast_helper<BaseMethod, ConcreteMethod, decltype(&ConcreteMethod::call), Args...>::call(context, args...);
|
||||
}
|
||||
|
||||
template <typename ConcreteMethod>
|
||||
static inline void virtual_method_register_override() {
|
||||
auto call = &virtual_method_override_instance<ConcreteMethod>;
|
||||
|
||||
virtual_methods::override_key<N> key;
|
||||
key.template collect<decltype(&ConcreteMethod::call)>();
|
||||
|
||||
virtual_method_get_override_map()[key] = call;
|
||||
}
|
||||
|
||||
template <typename ConcreteMethod, typename OtherBaseMethod>
|
||||
friend class virtual_override;
|
||||
|
||||
};
|
||||
|
||||
template <typename ConcreteMethod, typename BaseMethod>
|
||||
class virtual_override {
|
||||
protected:
|
||||
virtual ~virtual_override() {
|
||||
virtual_methods::dont_optimize_away(&_virtual_override_registrator);
|
||||
}
|
||||
|
||||
private:
|
||||
class virtual_override_registrator {
|
||||
public:
|
||||
inline virtual_override_registrator() {
|
||||
Assert(!virtual_methods::first_dispatch_fired());
|
||||
BaseMethod::template virtual_method_register_override<ConcreteMethod>();
|
||||
}
|
||||
|
||||
};
|
||||
static virtual_override_registrator _virtual_override_registrator;
|
||||
using virtual_override_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct<virtual_override_registrator*, &_virtual_override_registrator>;
|
||||
|
||||
};
|
||||
|
||||
template <typename ConcreteMethod, typename BaseMethod>
|
||||
typename virtual_override<ConcreteMethod, BaseMethod>::virtual_override_registrator virtual_override<ConcreteMethod, BaseMethod>::_virtual_override_registrator = {};
|
||||
|
||||
} // namespace base
|
|
@ -1,325 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
namespace base {
|
||||
|
||||
class has_weak_ptr;
|
||||
|
||||
namespace details {
|
||||
|
||||
struct alive_tracker {
|
||||
explicit alive_tracker(const has_weak_ptr *value) : value(value) {
|
||||
}
|
||||
|
||||
std::atomic<int> counter = 1;
|
||||
std::atomic<const has_weak_ptr*> value;
|
||||
};
|
||||
|
||||
inline alive_tracker *check_and_increment(alive_tracker *tracker) noexcept {
|
||||
if (tracker) {
|
||||
++tracker->counter;
|
||||
}
|
||||
return tracker;
|
||||
}
|
||||
|
||||
inline void decrement(alive_tracker *tracker) noexcept {
|
||||
if (tracker->counter.fetch_sub(1) == 0) {
|
||||
delete tracker;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename T>
|
||||
class weak_ptr;
|
||||
|
||||
class has_weak_ptr {
|
||||
public:
|
||||
has_weak_ptr() = default;
|
||||
has_weak_ptr(const has_weak_ptr &other) noexcept {
|
||||
}
|
||||
has_weak_ptr(has_weak_ptr &&other) noexcept {
|
||||
}
|
||||
has_weak_ptr &operator=(const has_weak_ptr &other) noexcept {
|
||||
return *this;
|
||||
}
|
||||
has_weak_ptr &operator=(has_weak_ptr &&other) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
~has_weak_ptr() {
|
||||
if (const auto alive = _alive.load()) {
|
||||
alive->value.store(nullptr);
|
||||
details::decrement(alive);
|
||||
}
|
||||
}
|
||||
|
||||
friend inline void invalidate_weak_ptrs(has_weak_ptr *object) {
|
||||
if (auto alive = object->_alive.load()) {
|
||||
if (object->_alive.compare_exchange_strong(alive, nullptr)) {
|
||||
alive->value.store(nullptr);
|
||||
details::decrement(alive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Child>
|
||||
friend class weak_ptr;
|
||||
|
||||
details::alive_tracker *incrementAliveTracker() const {
|
||||
auto current = _alive.load();
|
||||
if (!current) {
|
||||
auto alive = std::make_unique<details::alive_tracker>(this);
|
||||
if (_alive.compare_exchange_strong(current, alive.get())) {
|
||||
return alive.release();
|
||||
}
|
||||
}
|
||||
++current->counter;
|
||||
return current;
|
||||
}
|
||||
|
||||
mutable std::atomic<details::alive_tracker*> _alive = nullptr;
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class weak_ptr {
|
||||
public:
|
||||
weak_ptr() = default;
|
||||
weak_ptr(T *value)
|
||||
: _alive(value ? value->incrementAliveTracker() : nullptr) {
|
||||
}
|
||||
weak_ptr(const std::unique_ptr<T> &value)
|
||||
: weak_ptr(value.get()) {
|
||||
}
|
||||
weak_ptr(const std::shared_ptr<T> &value)
|
||||
: weak_ptr(value.get()) {
|
||||
}
|
||||
weak_ptr(const std::weak_ptr<T> &value)
|
||||
: weak_ptr(value.lock().get()) {
|
||||
}
|
||||
weak_ptr(const weak_ptr &other) noexcept
|
||||
: _alive(details::check_and_increment(other._alive)) {
|
||||
}
|
||||
weak_ptr(weak_ptr &&other) noexcept
|
||||
: _alive(std::exchange(other._alive, nullptr)) {
|
||||
}
|
||||
template <
|
||||
typename Other,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<T, Other> && !std::is_same_v<T, Other>>>
|
||||
weak_ptr(const weak_ptr<Other> &other) noexcept
|
||||
: _alive(details::check_and_increment(other._alive)) {
|
||||
}
|
||||
template <
|
||||
typename Other,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<T, Other> && !std::is_same_v<T, Other>>>
|
||||
weak_ptr(weak_ptr<Other> &&other) noexcept
|
||||
: _alive(std::exchange(other._alive, nullptr)) {
|
||||
}
|
||||
|
||||
weak_ptr &operator=(T *value) {
|
||||
reset(value);
|
||||
return *this;
|
||||
}
|
||||
weak_ptr &operator=(const std::unique_ptr<T> &value) {
|
||||
reset(value.get());
|
||||
return *this;
|
||||
}
|
||||
weak_ptr &operator=(const std::shared_ptr<T> &value) {
|
||||
reset(value.get());
|
||||
return *this;
|
||||
}
|
||||
weak_ptr &operator=(const std::weak_ptr<T> &value) {
|
||||
reset(value.lock().get());
|
||||
return *this;
|
||||
}
|
||||
weak_ptr &operator=(const weak_ptr &other) noexcept {
|
||||
if (_alive != other._alive) {
|
||||
destroy();
|
||||
_alive = details::check_and_increment(other._alive);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
weak_ptr &operator=(weak_ptr &&other) noexcept {
|
||||
if (_alive != other._alive) {
|
||||
destroy();
|
||||
_alive = std::exchange(other._alive, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template <
|
||||
typename Other,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<T, Other> && !std::is_same_v<T, Other>>>
|
||||
weak_ptr &operator=(const weak_ptr<Other> &other) noexcept {
|
||||
if (_alive != other._alive) {
|
||||
destroy();
|
||||
_alive = details::check_and_increment(other._alive);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template <
|
||||
typename Other,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<T, Other> && !std::is_same_v<T, Other>>>
|
||||
weak_ptr &operator=(weak_ptr<Other> &&other) noexcept {
|
||||
if (_alive != other._alive) {
|
||||
destroy();
|
||||
_alive = std::exchange(other._alive, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~weak_ptr() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
T *get() const noexcept {
|
||||
const auto strong = _alive ? _alive->value.load() : nullptr;
|
||||
if constexpr (std::is_const_v<T>) {
|
||||
return static_cast<T*>(strong);
|
||||
} else {
|
||||
return const_cast<T*>(static_cast<const T*>(strong));
|
||||
}
|
||||
}
|
||||
explicit operator bool() const noexcept {
|
||||
return (_alive && _alive->value);
|
||||
}
|
||||
T &operator*() const noexcept {
|
||||
return *get();
|
||||
}
|
||||
T *operator->() const noexcept {
|
||||
return get();
|
||||
}
|
||||
|
||||
void reset(T *value = nullptr) {
|
||||
if (get() != value) {
|
||||
destroy();
|
||||
_alive = value ? value->incrementAliveTracker() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void destroy() noexcept {
|
||||
if (_alive) {
|
||||
details::decrement(_alive);
|
||||
}
|
||||
}
|
||||
|
||||
details::alive_tracker *_alive = nullptr;
|
||||
|
||||
template <typename Other>
|
||||
friend class weak_ptr;
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline bool operator==(const weak_ptr<T> &pointer, std::nullptr_t) noexcept {
|
||||
return (pointer.get() == nullptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator==(std::nullptr_t, const weak_ptr<T> &pointer) noexcept {
|
||||
return (pointer == nullptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator!=(const weak_ptr<T> &pointer, std::nullptr_t) noexcept {
|
||||
return !(pointer == nullptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator!=(std::nullptr_t, const weak_ptr<T> &pointer) noexcept {
|
||||
return !(pointer == nullptr);
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename = std::enable_if_t<std::is_base_of_v<has_weak_ptr, T>>>
|
||||
weak_ptr<T> make_weak(T *value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename = std::enable_if_t<std::is_base_of_v<has_weak_ptr, T>>>
|
||||
weak_ptr<T> make_weak(const std::unique_ptr<T> &value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename = std::enable_if_t<std::is_base_of_v<has_weak_ptr, T>>>
|
||||
weak_ptr<T> make_weak(const std::shared_ptr<T> &value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename = std::enable_if_t<std::is_base_of_v<has_weak_ptr, T>>>
|
||||
weak_ptr<T> make_weak(const std::weak_ptr<T> &value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename T, typename Enable>
|
||||
struct guard_traits;
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<base::weak_ptr<T>, void> {
|
||||
static base::weak_ptr<T> create(const base::weak_ptr<T> &value) {
|
||||
return value;
|
||||
}
|
||||
static base::weak_ptr<T> create(base::weak_ptr<T> &&value) {
|
||||
return std::move(value);
|
||||
}
|
||||
static bool check(const base::weak_ptr<T> &guard) {
|
||||
return guard.get() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<
|
||||
T*,
|
||||
std::enable_if_t<
|
||||
std::is_base_of_v<base::has_weak_ptr, std::remove_cv_t<T>>>> {
|
||||
static base::weak_ptr<T> create(T *value) {
|
||||
return value;
|
||||
}
|
||||
static bool check(const base::weak_ptr<T> &guard) {
|
||||
return guard.get() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<
|
||||
gsl::not_null<T*>,
|
||||
std::enable_if_t<
|
||||
std::is_base_of_v<base::has_weak_ptr, std::remove_cv_t<T>>>> {
|
||||
static base::weak_ptr<T> create(gsl::not_null<T*> value) {
|
||||
return value.get();
|
||||
}
|
||||
static bool check(const base::weak_ptr<T> &guard) {
|
||||
return guard.get() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
|
@ -1,410 +0,0 @@
|
|||
/*
|
||||
WARNING! All changes made in this file will be lost!
|
||||
Created from 'colors.palette' by 'codegen_style'
|
||||
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "zip.h"
|
||||
#include "unzip.h"
|
||||
#include "logs.h"
|
||||
|
||||
#ifdef small
|
||||
#undef small
|
||||
#endif // small
|
||||
|
||||
namespace zlib {
|
||||
namespace internal {
|
||||
|
||||
class InMemoryFile {
|
||||
public:
|
||||
InMemoryFile(const QByteArray &data = QByteArray()) : _data(data) {
|
||||
}
|
||||
|
||||
zlib_filefunc_def funcs() {
|
||||
zlib_filefunc_def result;
|
||||
result.opaque = this;
|
||||
result.zopen_file = &InMemoryFile::Open;
|
||||
result.zerror_file = &InMemoryFile::Error;
|
||||
result.zread_file = &InMemoryFile::Read;
|
||||
result.zwrite_file = &InMemoryFile::Write;
|
||||
result.zclose_file = &InMemoryFile::Close;
|
||||
result.zseek_file = &InMemoryFile::Seek;
|
||||
result.ztell_file = &InMemoryFile::Tell;
|
||||
return result;
|
||||
}
|
||||
|
||||
int error() const {
|
||||
return _error;
|
||||
}
|
||||
|
||||
QByteArray result() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
private:
|
||||
voidpf open(const char *filename, int mode) {
|
||||
if (mode & ZLIB_FILEFUNC_MODE_WRITE) {
|
||||
if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
|
||||
_data.clear();
|
||||
}
|
||||
_position = _data.size();
|
||||
_data.reserve(2 * 1024 * 1024);
|
||||
} else if (mode & ZLIB_FILEFUNC_MODE_READ) {
|
||||
_position = 0;
|
||||
}
|
||||
_error = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
uLong read(voidpf stream, void* buf, uLong size) {
|
||||
uLong toRead = 0;
|
||||
if (!_error) {
|
||||
if (_data.size() > int(_position)) {
|
||||
toRead = qMin(size, uLong(_data.size() - _position));
|
||||
memcpy(buf, _data.constData() + _position, toRead);
|
||||
_position += toRead;
|
||||
}
|
||||
if (toRead < size) {
|
||||
_error = -1;
|
||||
}
|
||||
}
|
||||
return toRead;
|
||||
}
|
||||
|
||||
uLong write(voidpf stream, const void* buf, uLong size) {
|
||||
if (_data.size() < int(_position + size)) {
|
||||
_data.resize(_position + size);
|
||||
}
|
||||
memcpy(_data.data() + _position, buf, size);
|
||||
_position += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
int close(voidpf stream) {
|
||||
auto result = _error;
|
||||
_position = 0;
|
||||
_error = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int error(voidpf stream) const {
|
||||
return _error;
|
||||
}
|
||||
|
||||
long tell(voidpf stream) const {
|
||||
return _position;
|
||||
}
|
||||
|
||||
long seek(voidpf stream, uLong offset, int origin) {
|
||||
if (!_error) {
|
||||
switch (origin) {
|
||||
case ZLIB_FILEFUNC_SEEK_SET: _position = offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_CUR: _position += offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_END: _position = _data.size() + offset; break;
|
||||
}
|
||||
if (int(_position) > _data.size()) {
|
||||
_error = -1;
|
||||
}
|
||||
}
|
||||
return _error;
|
||||
}
|
||||
|
||||
static voidpf Open(voidpf opaque, const char* filename, int mode) {
|
||||
return static_cast<InMemoryFile*>(opaque)->open(filename, mode);
|
||||
}
|
||||
|
||||
static uLong Read(voidpf opaque, voidpf stream, void* buf, uLong size) {
|
||||
return static_cast<InMemoryFile*>(opaque)->read(stream, buf, size);
|
||||
}
|
||||
|
||||
static uLong Write(voidpf opaque, voidpf stream, const void* buf, uLong size) {
|
||||
return static_cast<InMemoryFile*>(opaque)->write(stream, buf, size);
|
||||
}
|
||||
|
||||
static int Close(voidpf opaque, voidpf stream) {
|
||||
return static_cast<InMemoryFile*>(opaque)->close(stream);
|
||||
}
|
||||
|
||||
static int Error(voidpf opaque, voidpf stream) {
|
||||
return static_cast<InMemoryFile*>(opaque)->error(stream);
|
||||
}
|
||||
|
||||
static long Tell(voidpf opaque, voidpf stream) {
|
||||
return static_cast<InMemoryFile*>(opaque)->tell(stream);
|
||||
}
|
||||
|
||||
static long Seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
|
||||
return static_cast<InMemoryFile*>(opaque)->seek(stream, offset, origin);
|
||||
}
|
||||
|
||||
uLong _position = 0;
|
||||
int _error = 0;
|
||||
QByteArray _data;
|
||||
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
constexpr int kCaseSensitive = 1;
|
||||
constexpr int kCaseInsensitive = 2;
|
||||
|
||||
class FileToRead {
|
||||
public:
|
||||
FileToRead(const QByteArray &content) : _data(content) {
|
||||
auto funcs = _data.funcs();
|
||||
if (!(_handle = unzOpen2(nullptr, &funcs))) {
|
||||
_error = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int getGlobalInfo(unz_global_info *pglobal_info) {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzGetGlobalInfo(_handle, pglobal_info) : -1;
|
||||
}
|
||||
return _error;
|
||||
}
|
||||
|
||||
int locateFile(const char *szFileName, int iCaseSensitivity) {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzLocateFile(_handle, szFileName, iCaseSensitivity) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int goToFirstFile() {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzGoToFirstFile(_handle) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int goToNextFile() {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzGoToNextFile(_handle) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int getCurrentFileInfo(
|
||||
unz_file_info *pfile_info,
|
||||
char *szFileName,
|
||||
uLong fileNameBufferSize,
|
||||
void *extraField,
|
||||
uLong extraFieldBufferSize,
|
||||
char *szComment,
|
||||
uLong commentBufferSize) {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzGetCurrentFileInfo(
|
||||
_handle,
|
||||
pfile_info,
|
||||
szFileName,
|
||||
fileNameBufferSize,
|
||||
extraField,
|
||||
extraFieldBufferSize,
|
||||
szComment,
|
||||
commentBufferSize
|
||||
) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
QString getCurrentFileName() {
|
||||
unz_file_info info = { 0 };
|
||||
constexpr auto kMaxName = 128;
|
||||
char name[kMaxName + 1] = { 0 };
|
||||
const auto result = getCurrentFileInfo(
|
||||
&info,
|
||||
name,
|
||||
kMaxName,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
0);
|
||||
return (result == UNZ_OK) ? QString::fromUtf8(name) : QString();
|
||||
}
|
||||
|
||||
int openCurrentFile() {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzOpenCurrentFile(_handle) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int readCurrentFile(voidp buf, unsigned len) {
|
||||
if (error() == UNZ_OK) {
|
||||
auto result = _handle ? unzReadCurrentFile(_handle, buf, len) : -1;
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
} else {
|
||||
_error = result;
|
||||
}
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int closeCurrentFile() {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzCloseCurrentFile(_handle) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
QByteArray readCurrentFileContent(int fileSizeLimit) {
|
||||
unz_file_info fileInfo = { 0 };
|
||||
if (getCurrentFileInfo(&fileInfo, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK) {
|
||||
LOG(("Error: could not get current file info in a zip file."));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
auto size = fileInfo.uncompressed_size;
|
||||
if (size > static_cast<uint32>(fileSizeLimit)) {
|
||||
if (_error == UNZ_OK) _error = -1;
|
||||
LOG(("Error: current file is too large (should be less than %1, got %2) in a zip file.").arg(fileSizeLimit).arg(size));
|
||||
return QByteArray();
|
||||
}
|
||||
if (openCurrentFile() != UNZ_OK) {
|
||||
LOG(("Error: could not open current file in a zip file."));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray result;
|
||||
result.resize(size);
|
||||
|
||||
auto couldRead = readCurrentFile(result.data(), size);
|
||||
if (couldRead != static_cast<int>(size)) {
|
||||
LOG(("Error: could not read current file in a zip file, got %1.").arg(couldRead));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
if (closeCurrentFile() != UNZ_OK) {
|
||||
LOG(("Error: could not close current file in a zip file."));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArray readFileContent(const char *szFileName, int iCaseSensitivity, int fileSizeLimit) {
|
||||
if (locateFile(szFileName, iCaseSensitivity) != UNZ_OK) {
|
||||
LOG(("Error: could not locate '%1' in a zip file.").arg(szFileName));
|
||||
return QByteArray();
|
||||
}
|
||||
return readCurrentFileContent(fileSizeLimit);
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (_handle && unzClose(_handle) != UNZ_OK && _error == UNZ_OK) {
|
||||
_error = -1;
|
||||
}
|
||||
_handle = nullptr;
|
||||
}
|
||||
|
||||
int error() const {
|
||||
if (auto dataError = _data.error()) {
|
||||
return dataError;
|
||||
}
|
||||
return _error;
|
||||
}
|
||||
|
||||
void clearError() {
|
||||
_error = UNZ_OK;
|
||||
}
|
||||
|
||||
~FileToRead() {
|
||||
close();
|
||||
}
|
||||
|
||||
private:
|
||||
internal::InMemoryFile _data;
|
||||
unzFile _handle = nullptr;
|
||||
int _error = 0;
|
||||
|
||||
};
|
||||
|
||||
class FileToWrite {
|
||||
public:
|
||||
FileToWrite() {
|
||||
auto funcs = _data.funcs();
|
||||
if (!(_handle = zipOpen2(nullptr, APPEND_STATUS_CREATE, nullptr, &funcs))) {
|
||||
_error = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int openNewFile(
|
||||
const char *filename,
|
||||
const zip_fileinfo *zipfi,
|
||||
const void *extrafield_local,
|
||||
uInt size_extrafield_local,
|
||||
const void* extrafield_global,
|
||||
uInt size_extrafield_global,
|
||||
const char* comment,
|
||||
int method,
|
||||
int level
|
||||
) {
|
||||
if (error() == ZIP_OK) {
|
||||
_error = _handle ? zipOpenNewFileInZip(
|
||||
_handle,
|
||||
filename,
|
||||
zipfi,
|
||||
extrafield_local,
|
||||
size_extrafield_local,
|
||||
extrafield_global,
|
||||
size_extrafield_global,
|
||||
comment,
|
||||
method,
|
||||
level
|
||||
) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int writeInFile(const void* buf, unsigned len) {
|
||||
if (error() == ZIP_OK) {
|
||||
_error = _handle ? zipWriteInFileInZip(_handle, buf, len) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int closeFile() {
|
||||
if (error() == ZIP_OK) {
|
||||
_error = _handle ? zipCloseFileInZip(_handle) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (_handle && zipClose(_handle, nullptr) != ZIP_OK && _error == ZIP_OK) {
|
||||
_error = -1;
|
||||
}
|
||||
_handle = nullptr;
|
||||
}
|
||||
|
||||
int error() const {
|
||||
if (auto dataError = _data.error()) {
|
||||
return dataError;
|
||||
}
|
||||
return _error;
|
||||
}
|
||||
|
||||
QByteArray result() const {
|
||||
return _data.result();
|
||||
}
|
||||
|
||||
~FileToWrite() {
|
||||
close();
|
||||
}
|
||||
|
||||
private:
|
||||
internal::InMemoryFile _data;
|
||||
zipFile _handle = nullptr;
|
||||
int _error = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace zlib
|
|
@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtCore/QVector>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <rpl/details/callable.h>
|
||||
#include "base/basic_types.h"
|
||||
#include "base/match_method.h"
|
||||
#include "base/flags.h"
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
template <typename SideEffect>
|
||||
class after_next_helper {
|
||||
public:
|
||||
template <typename OtherSideEffect>
|
||||
after_next_helper(OtherSideEffect &&method)
|
||||
: _method(std::forward<OtherSideEffect>(method)) {
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
auto operator()(producer<Value, Error, Generator> &&initial) {
|
||||
return make_producer<Value, Error>([
|
||||
initial = std::move(initial),
|
||||
method = std::move(_method)
|
||||
](const auto &consumer) mutable {
|
||||
return std::move(initial).start(
|
||||
[method = std::move(method), consumer](auto &&value) {
|
||||
auto copy = method;
|
||||
consumer.put_next_copy(value);
|
||||
details::callable_invoke(
|
||||
std::move(copy),
|
||||
std::forward<decltype(value)>(value));
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
SideEffect _method;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename SideEffect>
|
||||
inline auto after_next(SideEffect &&method)
|
||||
-> details::after_next_helper<std::decay_t<SideEffect>> {
|
||||
return details::after_next_helper<std::decay_t<SideEffect>>(
|
||||
std::forward<SideEffect>(method));
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
#include <rpl/filter.h>
|
||||
|
||||
namespace rpl {
|
||||
|
||||
template <typename SideEffect>
|
||||
inline auto before_next(SideEffect &&method) {
|
||||
return filter([method = std::forward<SideEffect>(method)](
|
||||
const auto &value) {
|
||||
method(value);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,349 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/optional.h"
|
||||
#include "base/variant.h"
|
||||
#include <rpl/map.h>
|
||||
#include <rpl/producer.h>
|
||||
#include <rpl/details/type_list.h>
|
||||
#include <rpl/details/callable.h>
|
||||
#include <rpl/mappers.h>
|
||||
#include <rpl/complete.h>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
template <typename ...Values>
|
||||
struct combine_state {
|
||||
combine_state() : accumulated(std::tuple<std::optional<Values>...>()) {
|
||||
}
|
||||
std::optional<std::tuple<std::optional<Values>...>> accumulated;
|
||||
std::optional<std::tuple<Values...>> latest;
|
||||
int invalid = sizeof...(Values);
|
||||
int working = sizeof...(Values);
|
||||
};
|
||||
|
||||
template <typename ...Values, std::size_t ...I>
|
||||
inline std::tuple<Values...> combine_make_first(
|
||||
std::tuple<std::optional<Values>...> &&accumulated,
|
||||
std::index_sequence<I...>) {
|
||||
return std::make_tuple(std::move(*std::get<I>(accumulated))...);
|
||||
}
|
||||
|
||||
template <size_t Index, typename consumer_type, typename ...Values>
|
||||
class combine_subscribe_one {
|
||||
public:
|
||||
combine_subscribe_one(
|
||||
const consumer_type &consumer,
|
||||
combine_state<Values...> *state)
|
||||
: _consumer(consumer)
|
||||
, _state(state) {
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
void subscribe(producer<Value, Error, Generator> &&producer) {
|
||||
_consumer.add_lifetime(std::move(producer).start(
|
||||
[consumer = _consumer, state = _state](Value &&value) {
|
||||
if (!state->accumulated) {
|
||||
std::get<Index>(*state->latest) = std::move(value);
|
||||
consumer.put_next_copy(*state->latest);
|
||||
} else {
|
||||
auto &accumulated = std::get<Index>(
|
||||
*state->accumulated);
|
||||
if (accumulated) {
|
||||
accumulated = std::move(value);
|
||||
} else {
|
||||
accumulated = std::move(value);
|
||||
if (!--state->invalid) {
|
||||
constexpr auto kArity = sizeof...(Values);
|
||||
state->latest = combine_make_first(
|
||||
std::move(*state->accumulated),
|
||||
std::make_index_sequence<kArity>());
|
||||
state->accumulated = std::nullopt;
|
||||
consumer.put_next_copy(*state->latest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [consumer = _consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer = _consumer, state = _state] {
|
||||
if (!--state->working) {
|
||||
consumer.put_done();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private:
|
||||
const consumer_type &_consumer;
|
||||
combine_state<Values...> *_state = nullptr;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename consumer_type,
|
||||
typename ...Values,
|
||||
typename ...Errors,
|
||||
typename ...Generators,
|
||||
std::size_t ...I>
|
||||
inline void combine_subscribe(
|
||||
const consumer_type &consumer,
|
||||
combine_state<Values...> *state,
|
||||
std::index_sequence<I...>,
|
||||
std::tuple<producer<Values, Errors, Generators>...> &&saved) {
|
||||
auto consume = { (
|
||||
combine_subscribe_one<I, consumer_type, Values...>(
|
||||
consumer,
|
||||
state
|
||||
).subscribe(std::get<I>(std::move(saved))), 0)... };
|
||||
(void)consume;
|
||||
}
|
||||
|
||||
template <typename ...Producers>
|
||||
class combine_implementation_helper;
|
||||
|
||||
template <typename ...Producers>
|
||||
combine_implementation_helper<std::decay_t<Producers>...>
|
||||
make_combine_implementation_helper(Producers &&...producers) {
|
||||
return combine_implementation_helper<std::decay_t<Producers>...>(
|
||||
std::forward<Producers>(producers)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Values,
|
||||
typename ...Errors,
|
||||
typename ...Generators>
|
||||
class combine_implementation_helper<producer<Values, Errors, Generators>...> {
|
||||
public:
|
||||
using CombinedValue = std::tuple<Values...>;
|
||||
using CombinedError = base::normalized_variant_t<Errors...>;
|
||||
|
||||
combine_implementation_helper(
|
||||
producer<Values, Errors, Generators> &&...producers)
|
||||
: _saved(std::make_tuple(std::move(producers)...)) {
|
||||
}
|
||||
|
||||
template <typename Handlers>
|
||||
lifetime operator()(const consumer<CombinedValue, CombinedError, Handlers> &consumer) {
|
||||
auto state = consumer.template make_state<
|
||||
combine_state<Values...>>();
|
||||
constexpr auto kArity = sizeof...(Values);
|
||||
combine_subscribe(
|
||||
consumer,
|
||||
state,
|
||||
std::make_index_sequence<kArity>(),
|
||||
std::move(_saved));
|
||||
|
||||
return lifetime();
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<producer<Values, Errors, Generators>...> _saved;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename ...Values,
|
||||
typename ...Errors,
|
||||
typename ...Generators>
|
||||
inline auto combine_implementation(
|
||||
producer<Values, Errors, Generators> &&...producers) {
|
||||
using CombinedValue = std::tuple<Values...>;
|
||||
using CombinedError = base::normalized_variant_t<Errors...>;
|
||||
|
||||
return make_producer<CombinedValue, CombinedError>(
|
||||
make_combine_implementation_helper(std::move(producers)...));
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
struct combine_just_producers : std::false_type {
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr bool combine_just_producers_v
|
||||
= combine_just_producers<Args...>::value;
|
||||
|
||||
template <
|
||||
typename ...Values,
|
||||
typename ...Errors,
|
||||
typename ...Generators>
|
||||
struct combine_just_producers<
|
||||
producer<Values, Errors, Generators>...>
|
||||
: std::true_type {
|
||||
};
|
||||
|
||||
template <typename ArgsList>
|
||||
struct combine_just_producers_list
|
||||
: type_list::extract_to_t<ArgsList, combine_just_producers> {
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
struct combine_result_type;
|
||||
|
||||
template <typename ...Args>
|
||||
using combine_result_type_t
|
||||
= typename combine_result_type<Args...>::type;
|
||||
|
||||
template <
|
||||
typename ...Values,
|
||||
typename ...Errors,
|
||||
typename ...Generators>
|
||||
struct combine_result_type<producer<Values, Errors, Generators>...> {
|
||||
using type = std::tuple<Values...>;
|
||||
};
|
||||
|
||||
template <typename ArgsList>
|
||||
struct combine_result_type_list
|
||||
: type_list::extract_to_t<ArgsList, combine_result_type> {
|
||||
};
|
||||
|
||||
template <typename ArgsList>
|
||||
using combine_result_type_list_t
|
||||
= typename combine_result_type_list<ArgsList>::type;
|
||||
|
||||
template <typename ArgsList>
|
||||
using combine_producers_no_mapper_t
|
||||
= type_list::chop_last_t<ArgsList>;
|
||||
|
||||
template <typename ArgsList>
|
||||
constexpr bool combine_is_good_mapper(std::true_type) {
|
||||
return is_callable_v<
|
||||
type_list::last_t<ArgsList>,
|
||||
combine_result_type_list_t<
|
||||
combine_producers_no_mapper_t<ArgsList>
|
||||
>>;
|
||||
}
|
||||
|
||||
template <typename ArgsList>
|
||||
constexpr bool combine_is_good_mapper(std::false_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ArgsList>
|
||||
struct combine_producers_with_mapper_list : std::bool_constant<
|
||||
combine_is_good_mapper<ArgsList>(
|
||||
combine_just_producers_list<
|
||||
combine_producers_no_mapper_t<ArgsList>
|
||||
>())> {
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
struct combine_producers_with_mapper
|
||||
: combine_producers_with_mapper_list<type_list::list<Args...>> {
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr bool combine_producers_with_mapper_v
|
||||
= combine_producers_with_mapper<Args...>::value;
|
||||
|
||||
template <typename ...Producers, std::size_t ...I>
|
||||
inline decltype(auto) combine_call(
|
||||
std::index_sequence<I...>,
|
||||
Producers &&...producers) {
|
||||
return combine_implementation(
|
||||
argument_mapper<I>::call(std::move(producers)...)...);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<
|
||||
details::combine_just_producers_v<Args...>
|
||||
|| details::combine_producers_with_mapper_v<Args...>>>
|
||||
inline decltype(auto) combine(Args &&...args) {
|
||||
if constexpr (details::combine_just_producers_v<Args...>) {
|
||||
return details::combine_implementation(std::move(args)...);
|
||||
} else if constexpr (details::combine_producers_with_mapper_v<Args...>) {
|
||||
constexpr auto kProducersCount = sizeof...(Args) - 1;
|
||||
return details::combine_call(
|
||||
std::make_index_sequence<kProducersCount>(),
|
||||
std::forward<Args>(args)...)
|
||||
| map(details::argument_mapper<kProducersCount>::call(
|
||||
std::forward<Args>(args)...));
|
||||
} else {
|
||||
static_assert(false_(args...), "Bad combine() call.");
|
||||
}
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Value>
|
||||
struct combine_vector_state {
|
||||
std::vector<std::optional<Value>> accumulated;
|
||||
std::vector<Value> latest;
|
||||
int invalid = 0;
|
||||
int working = 0;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
inline auto combine(
|
||||
std::vector<producer<Value, Error, Generator>> &&producers) {
|
||||
using state_type = details::combine_vector_state<Value>;
|
||||
return make_producer<std::vector<Value>, Error>([
|
||||
producers = std::move(producers)
|
||||
](const auto &consumer) mutable {
|
||||
auto count = producers.size();
|
||||
auto state = consumer.template make_state<state_type>();
|
||||
state->accumulated.resize(count);
|
||||
state->invalid = count;
|
||||
state->working = count;
|
||||
for (auto index = 0; index != count; ++index) {
|
||||
auto &producer = producers[index];
|
||||
consumer.add_lifetime(std::move(producer).start(
|
||||
[consumer, state, index](Value &&value) {
|
||||
if (state->accumulated.empty()) {
|
||||
state->latest[index] = std::move(value);
|
||||
consumer.put_next_copy(state->latest);
|
||||
} else if (state->accumulated[index]) {
|
||||
state->accumulated[index] = std::move(value);
|
||||
} else {
|
||||
state->accumulated[index] = std::move(value);
|
||||
if (!--state->invalid) {
|
||||
state->latest.reserve(
|
||||
state->accumulated.size());
|
||||
for (auto &&value : state->accumulated) {
|
||||
state->latest.push_back(
|
||||
std::move(*value));
|
||||
}
|
||||
details::take(state->accumulated);
|
||||
consumer.put_next_copy(state->latest);
|
||||
}
|
||||
}
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer, state] {
|
||||
if (!--state->working) {
|
||||
consumer.put_done();
|
||||
}
|
||||
}));
|
||||
}
|
||||
if (!count) {
|
||||
consumer.put_done();
|
||||
}
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Generator,
|
||||
typename Mapper>
|
||||
inline auto combine(
|
||||
std::vector<producer<Value, Error, Generator>> &&producers,
|
||||
Mapper &&mapper) {
|
||||
return combine(std::move(producers))
|
||||
| map(std::forward<Mapper>(mapper));
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
#include "base/optional.h"
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
class combine_previous_helper {
|
||||
public:
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
auto operator()(
|
||||
producer<Value, Error, Generator> &&initial) const {
|
||||
return make_producer<std::tuple<Value, Value>, Error>([
|
||||
initial = std::move(initial)
|
||||
](const auto &consumer) mutable {
|
||||
auto previous = consumer.template make_state<
|
||||
std::optional<Value>
|
||||
>();
|
||||
return std::move(initial).start(
|
||||
[consumer, previous](auto &&value) {
|
||||
if (auto &exists = *previous) {
|
||||
auto &existing = *exists;
|
||||
auto next = std::make_tuple(
|
||||
std::move(existing),
|
||||
value);
|
||||
consumer.put_next(std::move(next));
|
||||
existing = std::forward<decltype(value)>(
|
||||
value);
|
||||
} else {
|
||||
*previous = std::forward<decltype(value)>(
|
||||
value);
|
||||
}
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename DefaultValue>
|
||||
class combine_previous_with_default_helper {
|
||||
public:
|
||||
template <typename OtherValue>
|
||||
combine_previous_with_default_helper(OtherValue &&value)
|
||||
: _value(std::forward<OtherValue>(value)) {
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
auto operator()(producer<Value, Error, Generator> &&initial) {
|
||||
return make_producer<std::tuple<Value, Value>, Error>([
|
||||
initial = std::move(initial),
|
||||
value = Value(std::move(_value))
|
||||
](const auto &consumer) mutable {
|
||||
auto previous = consumer.template make_state<Value>(
|
||||
std::move(value));
|
||||
return std::move(initial).start(
|
||||
[consumer, previous](auto &&value) {
|
||||
auto &existing = *previous;
|
||||
auto next = std::make_tuple(
|
||||
std::move(existing),
|
||||
value);
|
||||
consumer.put_next(std::move(next));
|
||||
existing = std::forward<decltype(value)>(value);
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
DefaultValue _value;
|
||||
|
||||
};
|
||||
|
||||
template <typename DefaultValue>
|
||||
combine_previous_with_default_helper<std::decay_t<DefaultValue>>
|
||||
combine_previous_with_default(DefaultValue &&value) {
|
||||
return { std::forward<DefaultValue>(value) };
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline auto combine_previous()
|
||||
-> details::combine_previous_helper {
|
||||
return details::combine_previous_helper();
|
||||
}
|
||||
|
||||
template <typename DefaultValue>
|
||||
inline auto combine_previous(DefaultValue &&value) {
|
||||
return details::combine_previous_with_default(
|
||||
std::forward<DefaultValue>(value));
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
|
||||
template <typename Value = empty_value, typename Error = no_error>
|
||||
inline auto complete() {
|
||||
return make_producer<Value, Error>([](const auto &consumer) {
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/optional.h"
|
||||
#include "base/variant.h"
|
||||
#include <rpl/map.h>
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename GeneratorTest,
|
||||
typename GeneratorA,
|
||||
typename GeneratorB>
|
||||
inline auto conditional(
|
||||
rpl::producer<bool, Error, GeneratorTest> &&test,
|
||||
rpl::producer<Value, Error, GeneratorA> &&a,
|
||||
rpl::producer<Value, Error, GeneratorB> &&b) {
|
||||
return rpl::combine(
|
||||
std::move(test),
|
||||
std::move(a),
|
||||
std::move(b)
|
||||
) | rpl::map([](bool test, Value &&a, Value &&b) {
|
||||
return test ? std::move(a) : std::move(b);
|
||||
});
|
||||
//struct conditional_state {
|
||||
// std::optional<Value> a;
|
||||
// std::optional<Value> b;
|
||||
// char state = -1;
|
||||
// int working = 3;
|
||||
//};
|
||||
//return rpl::make_producer<Value, Error>([
|
||||
// test = std::move(test),
|
||||
// a = std::move(a),
|
||||
// b = std::move(b)
|
||||
//](const auto &consumer) mutable {
|
||||
// auto result = lifetime();
|
||||
// const auto state = result.make_state<conditional_state>();
|
||||
// result.add(std::move(test).start())
|
||||
// return result;
|
||||
//});
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,636 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <gsl/gsl_assert>
|
||||
#include <rpl/lifetime.h>
|
||||
#include <rpl/details/callable.h>
|
||||
|
||||
// GCC 7.2 can't handle not type-erased consumers.
|
||||
// It eats up 4GB RAM + 16GB swap on the unittest and dies.
|
||||
// Clang and Visual C++ both handle it without such problems.
|
||||
#if defined _DEBUG || defined COMPILER_GCC
|
||||
#define RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
#endif // _DEBUG || COMPILER_GCC
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename OnNext,
|
||||
typename OnError,
|
||||
typename OnDone>
|
||||
class consumer_handlers;
|
||||
|
||||
template <typename Value, typename Error>
|
||||
class type_erased_handlers {
|
||||
public:
|
||||
virtual bool put_next(Value &&value) = 0;
|
||||
virtual bool put_next_copy(const Value &value) = 0;
|
||||
virtual void put_error(Error &&error) = 0;
|
||||
virtual void put_error_copy(const Error &error) = 0;
|
||||
virtual void put_done() = 0;
|
||||
|
||||
bool add_lifetime(lifetime &&lifetime);
|
||||
|
||||
template <typename Type, typename... Args>
|
||||
Type *make_state(Args&& ...args);
|
||||
|
||||
void terminate();
|
||||
|
||||
virtual ~type_erased_handlers() = default;
|
||||
|
||||
protected:
|
||||
lifetime _lifetime;
|
||||
bool _terminated = false;
|
||||
|
||||
};
|
||||
|
||||
template <typename Handlers>
|
||||
struct is_type_erased_handlers
|
||||
: std::false_type {
|
||||
};
|
||||
|
||||
template <typename Value, typename Error>
|
||||
struct is_type_erased_handlers<type_erased_handlers<Value, Error>>
|
||||
: std::true_type {
|
||||
};
|
||||
|
||||
template <typename Handlers>
|
||||
constexpr bool is_type_erased_handlers_v
|
||||
= is_type_erased_handlers<Handlers>::value;
|
||||
|
||||
template <typename Value, typename Error, typename OnNext, typename OnError, typename OnDone>
|
||||
class consumer_handlers final
|
||||
: public type_erased_handlers<Value, Error> {
|
||||
public:
|
||||
template <
|
||||
typename OnNextOther,
|
||||
typename OnErrorOther,
|
||||
typename OnDoneOther>
|
||||
consumer_handlers(
|
||||
OnNextOther &&next,
|
||||
OnErrorOther &&error,
|
||||
OnDoneOther &&done)
|
||||
: _next(std::forward<OnNextOther>(next))
|
||||
, _error(std::forward<OnErrorOther>(error))
|
||||
, _done(std::forward<OnDoneOther>(done)) {
|
||||
}
|
||||
|
||||
bool put_next(Value &&value) final override;
|
||||
bool put_next_copy(const Value &value) final override;
|
||||
void put_error(Error &&error) final override;
|
||||
void put_error_copy(const Error &error) final override;
|
||||
void put_done() final override;
|
||||
|
||||
private:
|
||||
OnNext _next;
|
||||
OnError _error;
|
||||
OnDone _done;
|
||||
|
||||
};
|
||||
|
||||
template <typename Value, typename Error>
|
||||
inline bool type_erased_handlers<Value, Error>::add_lifetime(
|
||||
lifetime &&lifetime) {
|
||||
if (_terminated) {
|
||||
lifetime.destroy();
|
||||
return false;
|
||||
}
|
||||
_lifetime.add(std::move(lifetime));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
template <typename Type, typename... Args>
|
||||
inline Type *type_erased_handlers<Value, Error>::make_state(
|
||||
Args&& ...args) {
|
||||
if (_terminated) {
|
||||
return nullptr;
|
||||
}
|
||||
return _lifetime.make_state<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
inline void type_erased_handlers<Value, Error>::terminate() {
|
||||
if (!_terminated) {
|
||||
_terminated = true;
|
||||
_lifetime.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename OnNext,
|
||||
typename OnError,
|
||||
typename OnDone>
|
||||
bool consumer_handlers<
|
||||
Value,
|
||||
Error,
|
||||
OnNext,
|
||||
OnError,
|
||||
OnDone
|
||||
>::put_next(Value &&value) {
|
||||
if (this->_terminated) {
|
||||
return false;
|
||||
}
|
||||
auto handler = this->_next;
|
||||
details::callable_invoke(std::move(handler), std::move(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename OnNext,
|
||||
typename OnError,
|
||||
typename OnDone>
|
||||
bool consumer_handlers<
|
||||
Value,
|
||||
Error,
|
||||
OnNext,
|
||||
OnError,
|
||||
OnDone
|
||||
>::put_next_copy(const Value &value) {
|
||||
if (this->_terminated) {
|
||||
return false;
|
||||
}
|
||||
auto handler = this->_next;
|
||||
details::const_ref_call_invoke(std::move(handler), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename OnNext,
|
||||
typename OnError,
|
||||
typename OnDone>
|
||||
void consumer_handlers<
|
||||
Value,
|
||||
Error,
|
||||
OnNext,
|
||||
OnError,
|
||||
OnDone
|
||||
>::put_error(Error &&error) {
|
||||
if (!this->_terminated) {
|
||||
details::callable_invoke(
|
||||
std::move(this->_error),
|
||||
std::move(error));
|
||||
this->terminate();
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename OnNext,
|
||||
typename OnError,
|
||||
typename OnDone>
|
||||
void consumer_handlers<
|
||||
Value,
|
||||
Error,
|
||||
OnNext,
|
||||
OnError,
|
||||
OnDone
|
||||
>::put_error_copy(const Error &error) {
|
||||
if (!this->_terminated) {
|
||||
details::const_ref_call_invoke(
|
||||
std::move(this->_error),
|
||||
error);
|
||||
this->terminate();
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename OnNext,
|
||||
typename OnError,
|
||||
typename OnDone>
|
||||
void consumer_handlers<
|
||||
Value,
|
||||
Error,
|
||||
OnNext,
|
||||
OnError,
|
||||
OnDone
|
||||
>::put_done() {
|
||||
if (!this->_terminated) {
|
||||
std::move(this->_done)();
|
||||
this->terminate();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
struct no_value {
|
||||
no_value() = delete;
|
||||
};
|
||||
|
||||
struct no_error {
|
||||
no_error() = delete;
|
||||
};
|
||||
|
||||
struct empty_value {
|
||||
};
|
||||
|
||||
struct empty_error {
|
||||
};
|
||||
|
||||
template <
|
||||
typename Value = empty_value,
|
||||
typename Error = no_error,
|
||||
typename Handlers = details::type_erased_handlers<Value, Error>>
|
||||
class consumer;
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
class consumer_base {
|
||||
static constexpr bool is_type_erased
|
||||
= is_type_erased_handlers_v<Handlers>;
|
||||
|
||||
public:
|
||||
template <
|
||||
typename OnNext,
|
||||
typename OnError,
|
||||
typename OnDone>
|
||||
consumer_base(
|
||||
OnNext &&next,
|
||||
OnError &&error,
|
||||
OnDone &&done);
|
||||
|
||||
bool put_next(Value &&value) const;
|
||||
bool put_next_copy(const Value &value) const;
|
||||
bool put_next_forward(Value &&value) const {
|
||||
return put_next(std::move(value));
|
||||
}
|
||||
bool put_next_forward(const Value &value) const {
|
||||
return put_next_copy(value);
|
||||
}
|
||||
void put_error(Error &&error) const;
|
||||
void put_error_copy(const Error &error) const;
|
||||
void put_error_forward(Error &&error) const {
|
||||
return put_error(std::move(error));
|
||||
}
|
||||
void put_error_forward(const Error &error) const {
|
||||
return put_error_copy(error);
|
||||
}
|
||||
void put_done() const;
|
||||
|
||||
bool add_lifetime(lifetime &&lifetime) const;
|
||||
|
||||
template <typename Type, typename... Args>
|
||||
Type *make_state(Args&& ...args) const;
|
||||
|
||||
void terminate() const;
|
||||
auto terminator() const {
|
||||
return [self = *this] {
|
||||
self.terminate();
|
||||
};
|
||||
}
|
||||
|
||||
const details::type_erased_handlers<Value, Error> *comparable() const {
|
||||
return _handlers.get();
|
||||
}
|
||||
|
||||
private:
|
||||
template <
|
||||
typename OtherHandlers,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<Handlers, OtherHandlers>>>
|
||||
consumer_base(const std::shared_ptr<OtherHandlers> &handlers)
|
||||
: _handlers(handlers) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherHandlers,
|
||||
typename = std::enable_if_t<
|
||||
std::is_base_of_v<Handlers, OtherHandlers>>>
|
||||
consumer_base(std::shared_ptr<OtherHandlers> &&handlers)
|
||||
: _handlers(std::move(handlers)) {
|
||||
}
|
||||
|
||||
mutable std::shared_ptr<Handlers> _handlers;
|
||||
|
||||
bool handlers_put_next(Value &&value) const {
|
||||
if constexpr (is_type_erased) {
|
||||
return _handlers->put_next(std::move(value));
|
||||
} else {
|
||||
return _handlers->Handlers::put_next(std::move(value));
|
||||
}
|
||||
}
|
||||
bool handlers_put_next_copy(const Value &value) const {
|
||||
if constexpr (is_type_erased) {
|
||||
return _handlers->put_next_copy(value);
|
||||
} else {
|
||||
return _handlers->Handlers::put_next_copy(value);
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Handlers> take_handlers() const {
|
||||
return std::exchange(_handlers, nullptr);
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherValue,
|
||||
typename OtherError,
|
||||
typename OtherHandlers>
|
||||
friend class ::rpl::consumer;
|
||||
|
||||
};
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
template <typename OnNext, typename OnError, typename OnDone>
|
||||
inline consumer_base<Value, Error, Handlers>::consumer_base(
|
||||
OnNext &&next,
|
||||
OnError &&error,
|
||||
OnDone &&done)
|
||||
: _handlers(std::make_shared<consumer_handlers<
|
||||
Value,
|
||||
Error,
|
||||
std::decay_t<OnNext>,
|
||||
std::decay_t<OnError>,
|
||||
std::decay_t<OnDone>>>(
|
||||
std::forward<OnNext>(next),
|
||||
std::forward<OnError>(error),
|
||||
std::forward<OnDone>(done))) {
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
inline bool consumer_base<Value, Error, Handlers>::put_next(
|
||||
Value &&value) const {
|
||||
if (_handlers) {
|
||||
if (handlers_put_next(std::move(value))) {
|
||||
return true;
|
||||
}
|
||||
_handlers = nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
inline bool consumer_base<Value, Error, Handlers>::put_next_copy(
|
||||
const Value &value) const {
|
||||
if (_handlers) {
|
||||
if (handlers_put_next_copy(value)) {
|
||||
return true;
|
||||
}
|
||||
_handlers = nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
inline void consumer_base<Value, Error, Handlers>::put_error(
|
||||
Error &&error) const {
|
||||
if (_handlers) {
|
||||
if constexpr (is_type_erased) {
|
||||
take_handlers()->put_error(std::move(error));
|
||||
} else {
|
||||
take_handlers()->Handlers::put_error(std::move(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
inline void consumer_base<Value, Error, Handlers>::put_error_copy(
|
||||
const Error &error) const {
|
||||
if (_handlers) {
|
||||
if constexpr (is_type_erased) {
|
||||
take_handlers()->put_error_copy(error);
|
||||
} else {
|
||||
take_handlers()->Handlers::put_error_copy(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
inline void consumer_base<Value, Error, Handlers>::put_done() const {
|
||||
if (_handlers) {
|
||||
if constexpr (is_type_erased) {
|
||||
take_handlers()->put_done();
|
||||
} else {
|
||||
take_handlers()->Handlers::put_done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
inline bool consumer_base<Value, Error, Handlers>::add_lifetime(
|
||||
lifetime &&lifetime) const {
|
||||
if (!_handlers) {
|
||||
lifetime.destroy();
|
||||
return false;
|
||||
}
|
||||
if (_handlers->add_lifetime(std::move(lifetime))) {
|
||||
return true;
|
||||
}
|
||||
_handlers = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
template <typename Type, typename... Args>
|
||||
inline Type *consumer_base<Value, Error, Handlers>::make_state(
|
||||
Args&& ...args) const {
|
||||
if (!_handlers) {
|
||||
return nullptr;
|
||||
}
|
||||
if (auto result = _handlers->template make_state<Type>(
|
||||
std::forward<Args>(args)...)) {
|
||||
return result;
|
||||
}
|
||||
_handlers = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
inline void consumer_base<Value, Error, Handlers>::terminate() const {
|
||||
if (_handlers) {
|
||||
std::exchange(_handlers, nullptr)->terminate();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
using consumer_base_type_erased = consumer_base<
|
||||
Value,
|
||||
Error,
|
||||
details::type_erased_handlers<Value, Error>>;
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
constexpr bool is_specific_handlers_v = !std::is_same_v<
|
||||
details::type_erased_handlers<Value, Error>,
|
||||
Handlers
|
||||
> && std::is_base_of_v<
|
||||
details::type_erased_handlers<Value, Error>,
|
||||
Handlers
|
||||
>;
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Value, typename Error, typename Handlers>
|
||||
class consumer final
|
||||
: public details::consumer_base<Value, Error, Handlers> {
|
||||
using parent_type = details::consumer_base<
|
||||
Value,
|
||||
Error,
|
||||
Handlers>;
|
||||
|
||||
public:
|
||||
using parent_type::parent_type;
|
||||
|
||||
};
|
||||
|
||||
template <typename Value, typename Error>
|
||||
class consumer<Value, Error, details::type_erased_handlers<Value, Error>> final
|
||||
: public details::consumer_base_type_erased<Value, Error> {
|
||||
using parent_type = details::consumer_base_type_erased<
|
||||
Value,
|
||||
Error>;
|
||||
|
||||
public:
|
||||
using parent_type::parent_type;
|
||||
|
||||
template <
|
||||
typename Handlers,
|
||||
typename = std::enable_if_t<
|
||||
details::is_specific_handlers_v<Value, Error, Handlers>>>
|
||||
consumer(const details::consumer_base<Value, Error, Handlers> &other)
|
||||
: parent_type(other._handlers) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Handlers,
|
||||
typename = std::enable_if_t<
|
||||
details::is_specific_handlers_v<Value, Error, Handlers>>>
|
||||
consumer(details::consumer_base<Value, Error, Handlers> &&other)
|
||||
: parent_type(std::move(other._handlers)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Handlers,
|
||||
typename = std::enable_if_t<
|
||||
details::is_specific_handlers_v<Value, Error, Handlers>>>
|
||||
consumer &operator=(
|
||||
const details::consumer_base<Value, Error, Handlers> &other) {
|
||||
this->_handlers = other._handlers;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Handlers,
|
||||
typename = std::enable_if_t<
|
||||
details::is_specific_handlers_v<Value, Error, Handlers>>>
|
||||
consumer &operator=(
|
||||
details::consumer_base<Value, Error, Handlers> &&other) {
|
||||
this->_handlers = std::move(other._handlers);
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Handlers1,
|
||||
typename Handlers2>
|
||||
inline bool operator==(
|
||||
const consumer<Value, Error, Handlers1> &a,
|
||||
const consumer<Value, Error, Handlers2> &b) {
|
||||
return a.comparable() == b.comparable();
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Handlers1,
|
||||
typename Handlers2>
|
||||
inline bool operator<(
|
||||
const consumer<Value, Error, Handlers1> &a,
|
||||
const consumer<Value, Error, Handlers2> &b) {
|
||||
return a.comparable() < b.comparable();
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Handlers1,
|
||||
typename Handlers2>
|
||||
inline bool operator!=(
|
||||
const consumer<Value, Error, Handlers1> &a,
|
||||
const consumer<Value, Error, Handlers2> &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Handlers1,
|
||||
typename Handlers2>
|
||||
inline bool operator>(
|
||||
const consumer<Value, Error, Handlers1> &a,
|
||||
const consumer<Value, Error, Handlers2> &b) {
|
||||
return b < a;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Handlers1,
|
||||
typename Handlers2>
|
||||
inline bool operator<=(
|
||||
const consumer<Value, Error, Handlers1> &a,
|
||||
const consumer<Value, Error, Handlers2> &b) {
|
||||
return !(b < a);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Handlers1,
|
||||
typename Handlers2>
|
||||
inline bool operator>=(
|
||||
const consumer<Value, Error, Handlers1> &a,
|
||||
const consumer<Value, Error, Handlers2> &b) {
|
||||
return !(a < b);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename OnNext,
|
||||
typename OnError,
|
||||
typename OnDone,
|
||||
typename = std::enable_if_t<
|
||||
details::is_callable_v<OnNext, Value> &&
|
||||
details::is_callable_v<OnError, Error> &&
|
||||
details::is_callable_v<OnDone>>>
|
||||
#ifdef RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
inline consumer<Value, Error> make_consumer(
|
||||
#else // RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
inline auto make_consumer(
|
||||
#endif // !RPL_CONSUMER_TYPE_ERASED_ALWAYS
|
||||
OnNext &&next,
|
||||
OnError &&error,
|
||||
OnDone &&done) {
|
||||
return consumer<Value, Error, details::consumer_handlers<
|
||||
Value,
|
||||
Error,
|
||||
std::decay_t<OnNext>,
|
||||
std::decay_t<OnError>,
|
||||
std::decay_t<OnDone>>>(
|
||||
std::forward<OnNext>(next),
|
||||
std::forward<OnError>(error),
|
||||
std::forward<OnDone>(done));
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
|
||||
template <
|
||||
typename Creator,
|
||||
typename Value = typename decltype(std::declval<Creator>()())::value_type,
|
||||
typename Error = typename decltype(std::declval<Creator>()())::error_type>
|
||||
inline auto deferred(Creator &&creator) {
|
||||
return make_producer<Value, Error>([
|
||||
creator = std::forward<Creator>(creator)
|
||||
](const auto &consumer) mutable {
|
||||
return std::move(creator)().start_existing(consumer);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/build_config.h"
|
||||
#include <tuple>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
template <typename Arg>
|
||||
const Arg &const_ref_val() noexcept;
|
||||
|
||||
template <typename Arg>
|
||||
Arg &lvalue_ref_val() noexcept;
|
||||
|
||||
using false_t = char;
|
||||
struct true_t {
|
||||
false_t data[2];
|
||||
};
|
||||
static_assert(sizeof(false_t) != sizeof(true_t), "I can't work :(");
|
||||
|
||||
template <
|
||||
typename Method,
|
||||
typename ...Args,
|
||||
typename = decltype(std::declval<Method>()(
|
||||
std::declval<Args>()...))>
|
||||
true_t test_callable_plain(Method &&, Args &&...) noexcept;
|
||||
false_t test_callable_plain(...) noexcept;
|
||||
|
||||
template <typename Method, typename ...Args>
|
||||
struct is_callable_plain
|
||||
: std::bool_constant<(
|
||||
sizeof(test_callable_plain(
|
||||
std::declval<Method>(),
|
||||
std::declval<Args>()...
|
||||
)) == sizeof(true_t))> {
|
||||
};
|
||||
|
||||
template <typename Method, typename ...Args>
|
||||
constexpr bool is_callable_plain_v = is_callable_plain<Method, Args...>::value;
|
||||
|
||||
template <
|
||||
typename Method,
|
||||
typename ...Types,
|
||||
typename = decltype(std::declval<Method>()(
|
||||
std::declval<Types>()...))>
|
||||
true_t test_callable_tuple(
|
||||
Method &&,
|
||||
std::tuple<Types...> &&) noexcept;
|
||||
template <
|
||||
typename Method,
|
||||
typename ...Types,
|
||||
typename = decltype(std::declval<Method>()(
|
||||
const_ref_val<Types>()...))>
|
||||
true_t test_callable_tuple(
|
||||
Method &&,
|
||||
const std::tuple<Types...> &) noexcept;
|
||||
false_t test_callable_tuple(...) noexcept;
|
||||
|
||||
template <typename Method, typename Arg>
|
||||
constexpr bool is_callable_tuple_v = (sizeof(test_callable_tuple(
|
||||
std::declval<Method>(),
|
||||
std::declval<Arg>())) == sizeof(true_t));
|
||||
|
||||
template <typename Method, typename Arg>
|
||||
struct is_callable_tuple
|
||||
: std::bool_constant<
|
||||
is_callable_tuple_v<Method, Arg>> {
|
||||
};
|
||||
|
||||
template <typename Method, typename ...Args>
|
||||
struct is_callable;
|
||||
|
||||
template <typename Method>
|
||||
struct is_callable<Method>
|
||||
: std::bool_constant<
|
||||
is_callable_plain_v<Method>> {
|
||||
};
|
||||
|
||||
template <typename Method, typename Arg>
|
||||
struct is_callable<Method, Arg>
|
||||
: std::bool_constant<
|
||||
is_callable_plain_v<Method, Arg> ||
|
||||
is_callable_tuple_v<Method, Arg> ||
|
||||
is_callable_plain_v<Method>> {
|
||||
};
|
||||
|
||||
template <typename Method, typename ...Args>
|
||||
constexpr bool is_callable_v = is_callable<Method, Args...>::value;
|
||||
|
||||
template <typename Method, typename Arg>
|
||||
inline decltype(auto) callable_invoke(Method &&method, Arg &&arg) {
|
||||
if constexpr (is_callable_plain_v<Method, Arg>) {
|
||||
return std::forward<Method>(method)(std::forward<Arg>(arg));
|
||||
} else if constexpr (is_callable_tuple_v<Method, Arg>) {
|
||||
return std::apply(
|
||||
std::forward<Method>(method),
|
||||
std::forward<Arg>(arg));
|
||||
} else if constexpr (is_callable_v<Method>) {
|
||||
return std::forward<Method>(method)();
|
||||
} else {
|
||||
static_assert(false_(method, arg), "Bad callable_invoke() call.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Method, typename Arg>
|
||||
using callable_result = decltype(callable_invoke(
|
||||
std::declval<Method>(),
|
||||
std::declval<Arg>()));
|
||||
|
||||
template <
|
||||
typename Method,
|
||||
typename Arg,
|
||||
typename = decltype(std::declval<Method>()(
|
||||
const_ref_val<std::decay_t<Arg>>()))>
|
||||
true_t test_allows_const_ref(Method &&, Arg &&) noexcept;
|
||||
false_t test_allows_const_ref(...) noexcept;
|
||||
|
||||
template <typename Method, typename Arg>
|
||||
constexpr bool allows_const_ref_v = (sizeof(test_allows_const_ref(
|
||||
std::declval<Method>(),
|
||||
std::declval<Arg>())) == sizeof(true_t));
|
||||
|
||||
template <typename Method, typename Arg>
|
||||
inline decltype(auto) const_ref_call_invoke(
|
||||
Method &&method,
|
||||
const Arg &arg) {
|
||||
if constexpr (allows_const_ref_v<Method, Arg>) {
|
||||
return callable_invoke(std::forward<Method>(method), arg);
|
||||
} else {
|
||||
auto copy = arg;
|
||||
return callable_invoke(
|
||||
std::forward<Method>(method),
|
||||
std::move(copy));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace rpl
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace rpl {
|
||||
|
||||
template <typename Value1, typename Value2>
|
||||
struct superset_type;
|
||||
|
||||
template <typename Value1, typename Value2>
|
||||
using superset_type_t = typename superset_type<Value1, Value2>::type;
|
||||
|
||||
template <typename Value>
|
||||
struct superset_type<Value, Value> {
|
||||
using type = Value;
|
||||
};
|
||||
|
||||
} // namespace rpl
|
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
namespace type_list {
|
||||
|
||||
template <typename ...Types>
|
||||
struct list {
|
||||
};
|
||||
|
||||
template <typename Type, typename ...Types>
|
||||
struct list<Type, Types...> {
|
||||
using head = Type;
|
||||
using tail = list<Types...>;
|
||||
};
|
||||
|
||||
using empty_list = list<>;
|
||||
|
||||
template <typename TypeList>
|
||||
using head_t = typename TypeList::head;
|
||||
|
||||
template <typename TypeList>
|
||||
using tail_t = typename TypeList::tail;
|
||||
|
||||
template <typename Head, typename Tail>
|
||||
struct construct;
|
||||
|
||||
template <typename Head, typename Tail>
|
||||
using construct_t = typename construct<Head, Tail>::type;
|
||||
|
||||
template <typename Head, typename ...Types>
|
||||
struct construct<Head, list<Types...>> {
|
||||
using type = list<Head, Types...>;
|
||||
};
|
||||
|
||||
template <typename TypeList>
|
||||
struct size;
|
||||
|
||||
template <typename TypeList>
|
||||
constexpr std::size_t size_v = size<TypeList>::value;
|
||||
|
||||
template <typename ...Types>
|
||||
struct size<list<Types...>>
|
||||
: std::integral_constant<
|
||||
std::size_t,
|
||||
sizeof...(Types)> {
|
||||
};
|
||||
|
||||
template <typename TypeList>
|
||||
constexpr bool empty_v = (size_v<TypeList> == 0);
|
||||
|
||||
template <typename TypeList>
|
||||
struct empty : std::bool_constant<empty_v<TypeList>> {
|
||||
};
|
||||
|
||||
template <std::size_t Index, typename TypeList>
|
||||
struct get;
|
||||
|
||||
template <std::size_t Index, typename TypeList>
|
||||
using get_t = typename get<Index, TypeList>::type;
|
||||
|
||||
template <std::size_t Index, typename TypeList>
|
||||
struct get {
|
||||
using type = get_t<Index - 1, tail_t<TypeList>>;
|
||||
};
|
||||
|
||||
template <typename TypeList>
|
||||
struct get<0, TypeList> {
|
||||
using type = head_t<TypeList>;
|
||||
};
|
||||
|
||||
template <typename TypeList1, typename TypeList2>
|
||||
struct concat;
|
||||
|
||||
template <typename TypeList1, typename TypeList2>
|
||||
using concat_t = typename concat<TypeList1, TypeList2>::type;
|
||||
|
||||
template <typename ...Types1, typename ...Types2>
|
||||
struct concat<list<Types1...>, list<Types2...>> {
|
||||
using type = list<Types1..., Types2...>;
|
||||
};
|
||||
|
||||
template <typename TypeList, typename Type>
|
||||
struct remove_all;
|
||||
|
||||
template <typename TypeList, typename Type>
|
||||
using remove_all_t = typename remove_all<TypeList, Type>::type;
|
||||
|
||||
template <typename TypeList, typename Type>
|
||||
struct remove_all {
|
||||
using head = head_t<TypeList>;
|
||||
using tail = tail_t<TypeList>;
|
||||
using clean_tail = remove_all_t<tail, Type>;
|
||||
using type = std::conditional_t<
|
||||
std::is_same_v<head, Type>,
|
||||
clean_tail,
|
||||
construct_t<head, clean_tail>>;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct remove_all<empty_list, Type> {
|
||||
using type = empty_list;
|
||||
};
|
||||
|
||||
template <typename TypeList>
|
||||
struct last;
|
||||
|
||||
template <typename TypeList>
|
||||
using last_t = typename last<TypeList>::type;
|
||||
|
||||
template <typename TypeList>
|
||||
struct last {
|
||||
using type = last_t<tail_t<TypeList>>;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct last<list<Type>> {
|
||||
using type = Type;
|
||||
};
|
||||
|
||||
template <typename TypeList>
|
||||
struct chop_last;
|
||||
|
||||
template <typename TypeList>
|
||||
using chop_last_t = typename chop_last<TypeList>::type;
|
||||
|
||||
template <typename TypeList>
|
||||
struct chop_last {
|
||||
using type = construct_t<
|
||||
head_t<TypeList>,
|
||||
chop_last_t<tail_t<TypeList>>>;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct chop_last<list<Type>> {
|
||||
using type = empty_list;
|
||||
};
|
||||
|
||||
template <typename TypeList>
|
||||
struct distinct;
|
||||
|
||||
template <typename TypeList>
|
||||
using distinct_t = typename distinct<TypeList>::type;
|
||||
|
||||
template <typename TypeList>
|
||||
struct distinct {
|
||||
using type = construct_t<
|
||||
head_t<TypeList>,
|
||||
distinct_t<
|
||||
remove_all_t<tail_t<TypeList>, head_t<TypeList>>>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct distinct<empty_list> {
|
||||
using type = empty_list;
|
||||
};
|
||||
|
||||
template <typename TypeList, template <typename ...> typename To>
|
||||
struct extract_to;
|
||||
|
||||
template <typename TypeList, template <typename ...> typename To>
|
||||
using extract_to_t = typename extract_to<TypeList, To>::type;
|
||||
|
||||
template <typename ...Types, template <typename ...> typename To>
|
||||
struct extract_to<list<Types...>, To> {
|
||||
using type = To<Types...>;
|
||||
};
|
||||
|
||||
} // namespace type_list
|
||||
} // namespace details
|
||||
} // namespace rpl
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
#include "base/optional.h"
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
class distinct_until_changed_helper {
|
||||
public:
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
auto operator()(
|
||||
producer<Value, Error, Generator> &&initial) const {
|
||||
return make_producer<Value, Error>([
|
||||
initial = std::move(initial)
|
||||
](const auto &consumer) mutable {
|
||||
auto previous = consumer.template make_state<
|
||||
std::optional<Value>
|
||||
>();
|
||||
return std::move(initial).start(
|
||||
[consumer, previous](auto &&value) {
|
||||
if (!(*previous) || (**previous) != value) {
|
||||
*previous = value;
|
||||
consumer.put_next_forward(std::forward<decltype(value)>(value));
|
||||
}
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline auto distinct_until_changed()
|
||||
-> details::distinct_until_changed_helper {
|
||||
return details::distinct_until_changed_helper();
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,293 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
#include <rpl/range.h>
|
||||
#include <rpl/then.h>
|
||||
#include <rpl/range.h>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include "base/assertion.h"
|
||||
#include "base/index_based_iterator.h"
|
||||
|
||||
namespace rpl {
|
||||
|
||||
// Currently not thread-safe :(
|
||||
|
||||
template <typename Value = empty_value, typename Error = no_error>
|
||||
class event_stream {
|
||||
public:
|
||||
event_stream();
|
||||
event_stream(event_stream &&other);
|
||||
event_stream &operator=(event_stream &&other);
|
||||
|
||||
template <typename OtherValue>
|
||||
void fire_forward(OtherValue &&value) const;
|
||||
void fire(Value &&value) const {
|
||||
return fire_forward(std::move(value));
|
||||
}
|
||||
void fire_copy(const Value &value) const {
|
||||
return fire_forward(value);
|
||||
}
|
||||
|
||||
template <typename OtherError>
|
||||
void fire_error_forward(OtherError &&error) const;
|
||||
void fire_error(Error &&error) const {
|
||||
return fire_error_forward(std::move(error));
|
||||
}
|
||||
void fire_error_copy(const Error &error) const {
|
||||
return fire_error_forward(error);
|
||||
}
|
||||
|
||||
void fire_done() const;
|
||||
|
||||
#if defined _MSC_VER && _MSC_VER >= 1914 && _MSC_VER < 1916
|
||||
producer<Value, Error> events() const {
|
||||
#else // _MSC_VER >= 1914 && _MSC_VER < 1916
|
||||
auto events() const {
|
||||
#endif // _MSC_VER >= 1914 && _MSC_VER < 1916
|
||||
return make_producer<Value, Error>([weak = make_weak()](
|
||||
const auto &consumer) {
|
||||
if (const auto strong = weak.lock()) {
|
||||
auto result = [weak, consumer] {
|
||||
if (const auto strong = weak.lock()) {
|
||||
const auto it = std::find(
|
||||
strong->consumers.begin(),
|
||||
strong->consumers.end(),
|
||||
consumer);
|
||||
if (it != strong->consumers.end()) {
|
||||
it->terminate();
|
||||
}
|
||||
}
|
||||
};
|
||||
strong->consumers.push_back(std::move(consumer));
|
||||
return lifetime(std::move(result));
|
||||
}
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
auto events_starting_with(Value &&value) const {
|
||||
return single<Value&&, Error>(std::move(value)) | then(events());
|
||||
}
|
||||
auto events_starting_with_copy(const Value &value) const {
|
||||
return single<const Value&, Error>(value) | then(events());
|
||||
}
|
||||
bool has_consumers() const {
|
||||
return (_data != nullptr) && !_data->consumers.empty();
|
||||
}
|
||||
|
||||
~event_stream();
|
||||
|
||||
private:
|
||||
struct Data {
|
||||
std::vector<consumer<Value, Error>> consumers;
|
||||
int depth = 0;
|
||||
};
|
||||
std::weak_ptr<Data> make_weak() const;
|
||||
|
||||
mutable std::shared_ptr<Data> _data;
|
||||
|
||||
};
|
||||
|
||||
template <typename Value, typename Error>
|
||||
inline event_stream<Value, Error>::event_stream() {
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
inline event_stream<Value, Error>::event_stream(event_stream &&other)
|
||||
: _data(details::take(other._data)) {
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
inline event_stream<Value, Error> &event_stream<Value, Error>::operator=(
|
||||
event_stream &&other) {
|
||||
if (this != &other) {
|
||||
std::swap(_data, other._data);
|
||||
other.fire_done();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
template <typename OtherValue>
|
||||
inline void event_stream<Value, Error>::fire_forward(
|
||||
OtherValue &&value) const {
|
||||
if (!_data) {
|
||||
return;
|
||||
}
|
||||
const auto copy = _data;
|
||||
auto &consumers = copy->consumers;
|
||||
if (consumers.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
++copy->depth;
|
||||
const auto begin = base::index_based_begin(consumers);
|
||||
const auto end = base::index_based_end(consumers);
|
||||
|
||||
// Copy value for every consumer except the last.
|
||||
const auto prev = end - 1;
|
||||
auto staleFrom = std::remove_if(begin, prev, [&](const auto &consumer) {
|
||||
return !consumer.put_next_copy(value);
|
||||
});
|
||||
|
||||
// Perhaps move value for the last consumer.
|
||||
if (prev->put_next_forward(std::forward<OtherValue>(value))) {
|
||||
if (staleFrom != prev) {
|
||||
*staleFrom++ = std::move(*prev);
|
||||
} else {
|
||||
++staleFrom;
|
||||
}
|
||||
}
|
||||
|
||||
if (staleFrom != end) {
|
||||
// Move new consumers.
|
||||
const auto newEnd = base::index_based_end(consumers);
|
||||
if (newEnd != end) {
|
||||
Assert(newEnd > end);
|
||||
for (auto i = end; i != newEnd;) {
|
||||
*staleFrom++ = *i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Erase stale consumers.
|
||||
if (copy->depth == 1) {
|
||||
consumers.erase(staleFrom.base(), consumers.end());
|
||||
}
|
||||
}
|
||||
--copy->depth;
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
template <typename OtherError>
|
||||
inline void event_stream<Value, Error>::fire_error_forward(
|
||||
OtherError &&error) const {
|
||||
if (!_data) {
|
||||
return;
|
||||
}
|
||||
const auto data = std::move(_data);
|
||||
const auto &consumers = data->consumers;
|
||||
if (consumers.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto begin = base::index_based_begin(consumers);
|
||||
const auto end = base::index_based_end(consumers);
|
||||
|
||||
// Copy error for every consumer except the last.
|
||||
const auto prev = end - 1;
|
||||
std::for_each(begin, prev, [&](const auto &consumer) {
|
||||
consumer.put_error_copy(error);
|
||||
});
|
||||
|
||||
// Perhaps move error for the last consumer.
|
||||
prev->put_error_forward(std::forward<OtherError>(error));
|
||||
|
||||
// Just drop any new consumers.
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
void event_stream<Value, Error>::fire_done() const {
|
||||
if (const auto data = details::take(_data)) {
|
||||
for (const auto &consumer : data->consumers) {
|
||||
consumer.put_done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
inline auto event_stream<Value, Error>::make_weak() const
|
||||
-> std::weak_ptr<Data> {
|
||||
if (!_data) {
|
||||
_data = std::make_shared<Data>();
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
inline event_stream<Value, Error>::~event_stream() {
|
||||
fire_done();
|
||||
}
|
||||
|
||||
template <typename Value, typename Error>
|
||||
inline auto start_to_stream(
|
||||
event_stream<Value, Error> &stream,
|
||||
lifetime &alive_while) {
|
||||
if constexpr (std::is_same_v<Error, no_error>) {
|
||||
return start_with_next_done([&](auto &&value) {
|
||||
stream.fire_forward(std::forward<decltype(value)>(value));
|
||||
}, [&] {
|
||||
stream.fire_done();
|
||||
}, alive_while);
|
||||
} else {
|
||||
return start_with_next_error_done([&](auto &&value) {
|
||||
stream.fire_forward(std::forward<decltype(value)>(value));
|
||||
}, [&](auto &&error) {
|
||||
stream.fire_error_forward(std::forward<decltype(error)>(error));
|
||||
}, [&] {
|
||||
stream.fire_done();
|
||||
}, alive_while);
|
||||
}
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
class start_spawning_helper {
|
||||
public:
|
||||
start_spawning_helper(lifetime &alive_while)
|
||||
: _lifetime(alive_while) {
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
auto operator()(producer<Value, Error, Generator> &&initial) {
|
||||
auto stream = _lifetime.make_state<event_stream<Value, Error>>();
|
||||
auto values = std::vector<Value>();
|
||||
if constexpr (std::is_same_v<Error, rpl::no_error>) {
|
||||
auto collecting = stream->events().start(
|
||||
[&](Value &&value) { values.push_back(std::move(value)); },
|
||||
[](const Error &error) {},
|
||||
[] {});
|
||||
std::move(initial) | start_to_stream(*stream, _lifetime);
|
||||
collecting.destroy();
|
||||
|
||||
return vector(std::move(values)) | then(stream->events());
|
||||
} else {
|
||||
auto maybeError = std::optional<Error>();
|
||||
auto collecting = stream->events().start(
|
||||
[&](Value && value) { values.push_back(std::move(value)); },
|
||||
[&](Error &&error) { maybeError = std::move(error); },
|
||||
[] {});
|
||||
std::move(initial) | start_to_stream(*stream, _lifetime);
|
||||
collecting.destroy();
|
||||
|
||||
if (maybeError.has_value()) {
|
||||
return rpl::producer<Value, Error>([
|
||||
error = std::move(*maybeError)
|
||||
](const auto &consumer) mutable {
|
||||
consumer.put_error(std::move(error));
|
||||
});
|
||||
}
|
||||
return rpl::producer<Value, Error>(vector<Value, Error>(
|
||||
std::move(values)
|
||||
) | then(stream->events()));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
lifetime &_lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline auto start_spawning(lifetime &alive_while)
|
||||
-> details::start_spawning_helper {
|
||||
return details::start_spawning_helper(alive_while);
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
|
||||
template <typename Value, typename Error>
|
||||
inline auto fail(Error &&error) {
|
||||
return make_producer<Value, std::decay_t<Error>>([
|
||||
error = std::forward<Error>(error)
|
||||
](const auto &consumer) mutable {
|
||||
consumer.put_error(std::move(error));
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
#include <rpl/combine.h>
|
||||
#include <rpl/mappers.h>
|
||||
#include "base/optional.h"
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
template <typename Predicate>
|
||||
class filter_helper {
|
||||
public:
|
||||
template <typename OtherPredicate>
|
||||
filter_helper(OtherPredicate &&predicate)
|
||||
: _predicate(std::forward<OtherPredicate>(predicate)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Generator,
|
||||
typename = std::enable_if_t<
|
||||
details::is_callable_v<Predicate, Value>>>
|
||||
auto operator()(producer<Value, Error, Generator> &&initial) {
|
||||
return make_producer<Value, Error>([
|
||||
initial = std::move(initial),
|
||||
predicate = std::move(_predicate)
|
||||
](const auto &consumer) mutable {
|
||||
return std::move(initial).start(
|
||||
[
|
||||
consumer,
|
||||
predicate = std::move(predicate)
|
||||
](auto &&value) {
|
||||
const auto &immutable = value;
|
||||
if (details::callable_invoke(
|
||||
predicate,
|
||||
immutable)
|
||||
) {
|
||||
consumer.put_next_forward(
|
||||
std::forward<decltype(value)>(value));
|
||||
}
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
Predicate _predicate;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Predicate>
|
||||
inline auto filter(Predicate &&predicate)
|
||||
-> details::filter_helper<std::decay_t<Predicate>> {
|
||||
return details::filter_helper<std::decay_t<Predicate>>(
|
||||
std::forward<Predicate>(predicate));
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename FilterError, typename FilterGenerator>
|
||||
class filter_helper<producer<bool, FilterError, FilterGenerator>> {
|
||||
public:
|
||||
filter_helper(
|
||||
producer<bool, FilterError, FilterGenerator> &&filterer)
|
||||
: _filterer(std::move(filterer)) {
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
auto operator()(producer<Value, Error, Generator> &&initial) {
|
||||
using namespace mappers;
|
||||
return combine(std::move(initial), std::move(_filterer))
|
||||
| filter(_2)
|
||||
| map(_1_of_two);
|
||||
}
|
||||
|
||||
private:
|
||||
producer<bool, FilterError, FilterGenerator> _filterer;
|
||||
|
||||
};
|
||||
|
||||
template <typename Value>
|
||||
inline const Value &deref_optional_helper(
|
||||
const std::optional<Value> &value) {
|
||||
return *value;
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
inline Value &&deref_optional_helper(
|
||||
std::optional<Value> &&value) {
|
||||
return std::move(*value);
|
||||
}
|
||||
|
||||
class filter_optional_helper {
|
||||
public:
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
auto operator()(producer<
|
||||
std::optional<Value>,
|
||||
Error,
|
||||
Generator> &&initial) const {
|
||||
return make_producer<Value, Error>([
|
||||
initial = std::move(initial)
|
||||
](const auto &consumer) mutable {
|
||||
return std::move(initial).start(
|
||||
[consumer](auto &&value) {
|
||||
if (value) {
|
||||
consumer.put_next_forward(
|
||||
deref_optional_helper(
|
||||
std::forward<decltype(value)>(
|
||||
value)));
|
||||
}
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline auto filter_optional()
|
||||
-> details::filter_optional_helper {
|
||||
return details::filter_optional_helper();
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
class flatten_latest_helper {
|
||||
public:
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Generator,
|
||||
typename MetaGenerator>
|
||||
auto operator()(producer<
|
||||
producer<Value, Error, Generator>,
|
||||
Error,
|
||||
MetaGenerator> &&initial) const {
|
||||
return make_producer<Value, Error>([
|
||||
initial = std::move(initial)
|
||||
](const auto &consumer) mutable {
|
||||
auto state = consumer.template make_state<State>();
|
||||
return std::move(initial).start(
|
||||
[consumer, state](producer<Value, Error> &&inner) {
|
||||
state->finished = false;
|
||||
state->alive = lifetime();
|
||||
std::move(inner).start(
|
||||
[consumer](auto &&value) {
|
||||
consumer.put_next_forward(std::forward<decltype(value)>(value));
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(std::forward<decltype(error)>(error));
|
||||
}, [consumer, state] {
|
||||
if (state->finished) {
|
||||
consumer.put_done();
|
||||
} else {
|
||||
state->finished = true;
|
||||
}
|
||||
}, state->alive);
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(std::forward<decltype(error)>(error));
|
||||
}, [consumer, state] {
|
||||
if (state->finished) {
|
||||
consumer.put_done();
|
||||
} else {
|
||||
state->finished = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
struct State {
|
||||
lifetime alive;
|
||||
bool finished = false;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline auto flatten_latest()
|
||||
-> details::flatten_latest_helper {
|
||||
return details::flatten_latest_helper();
|
||||
}
|
||||
|
||||
} // namespace rpl
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/unique_function.h"
|
||||
#include <vector>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
template <typename Type>
|
||||
inline Type take(Type &value) {
|
||||
return std::exchange(value, Type{});
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
class lifetime {
|
||||
public:
|
||||
lifetime() = default;
|
||||
lifetime(lifetime &&other);
|
||||
lifetime &operator=(lifetime &&other);
|
||||
~lifetime() { destroy(); }
|
||||
|
||||
template <typename Destroy, typename = decltype(std::declval<Destroy>()())>
|
||||
lifetime(Destroy &&destroy);
|
||||
|
||||
explicit operator bool() const { return !_callbacks.empty(); }
|
||||
|
||||
template <typename Destroy, typename = decltype(std::declval<Destroy>()())>
|
||||
void add(Destroy &&destroy);
|
||||
void add(lifetime &&other);
|
||||
void destroy();
|
||||
|
||||
template <typename Type, typename... Args>
|
||||
Type *make_state(Args&& ...args) {
|
||||
const auto result = new Type(std::forward<Args>(args)...);
|
||||
add([=] {
|
||||
static_assert(sizeof(Type) > 0, "Can't delete unknown type.");
|
||||
delete result;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<base::unique_function<void()>> _callbacks;
|
||||
|
||||
};
|
||||
|
||||
inline lifetime::lifetime(lifetime &&other)
|
||||
: _callbacks(details::take(other._callbacks)) {
|
||||
}
|
||||
|
||||
inline lifetime &lifetime::operator=(lifetime &&other) {
|
||||
std::swap(_callbacks, other._callbacks);
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Destroy, typename>
|
||||
inline lifetime::lifetime(Destroy &&destroy) {
|
||||
_callbacks.emplace_back(std::forward<Destroy>(destroy));
|
||||
}
|
||||
|
||||
template <typename Destroy, typename>
|
||||
inline void lifetime::add(Destroy &&destroy) {
|
||||
_callbacks.emplace_back(std::forward<Destroy>(destroy));
|
||||
}
|
||||
|
||||
inline void lifetime::add(lifetime &&other) {
|
||||
auto callbacks = details::take(other._callbacks);
|
||||
_callbacks.insert(
|
||||
_callbacks.end(),
|
||||
std::make_move_iterator(callbacks.begin()),
|
||||
std::make_move_iterator(callbacks.end()));
|
||||
}
|
||||
|
||||
inline void lifetime::destroy() {
|
||||
auto callbacks = details::take(_callbacks);
|
||||
for (auto i = callbacks.rbegin(), e = callbacks.rend(); i != e; ++i) {
|
||||
(*i)();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,211 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
template <
|
||||
typename Transform,
|
||||
typename NewValue,
|
||||
typename Error,
|
||||
typename Handlers>
|
||||
class map_transform_helper {
|
||||
public:
|
||||
map_transform_helper(
|
||||
Transform &&transform,
|
||||
const consumer<NewValue, Error, Handlers> &consumer)
|
||||
: _consumer(consumer)
|
||||
, _transform(std::move(transform)) {
|
||||
}
|
||||
template <
|
||||
typename OtherValue,
|
||||
typename = std::enable_if_t<
|
||||
std::is_rvalue_reference_v<OtherValue&&>>>
|
||||
void operator()(OtherValue &&value) const {
|
||||
_consumer.put_next_forward(
|
||||
details::callable_invoke(_transform, std::move(value)));
|
||||
}
|
||||
template <
|
||||
typename OtherValue,
|
||||
typename = decltype(
|
||||
std::declval<Transform>()(const_ref_val<OtherValue>()))>
|
||||
void operator()(const OtherValue &value) const {
|
||||
_consumer.put_next_forward(
|
||||
details::callable_invoke(_transform, value));
|
||||
}
|
||||
|
||||
private:
|
||||
consumer<NewValue, Error, Handlers> _consumer;
|
||||
Transform _transform;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename Transform,
|
||||
typename NewValue,
|
||||
typename Error,
|
||||
typename Handlers,
|
||||
typename = std::enable_if_t<
|
||||
std::is_rvalue_reference_v<Transform&&>>>
|
||||
inline map_transform_helper<Transform, NewValue, Error, Handlers>
|
||||
map_transform(
|
||||
Transform &&transform,
|
||||
const consumer<NewValue, Error, Handlers> &consumer) {
|
||||
return { std::move(transform), consumer };
|
||||
}
|
||||
|
||||
template <typename Transform>
|
||||
class map_helper {
|
||||
public:
|
||||
template <typename OtherTransform>
|
||||
map_helper(OtherTransform &&transform)
|
||||
: _transform(std::forward<OtherTransform>(transform)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Generator,
|
||||
typename NewValue = details::callable_result<
|
||||
Transform,
|
||||
Value>>
|
||||
auto operator()(producer<Value, Error, Generator> &&initial) {
|
||||
return make_producer<NewValue, Error>([
|
||||
initial = std::move(initial),
|
||||
transform = std::move(_transform)
|
||||
](const auto &consumer) mutable {
|
||||
return std::move(initial).start(
|
||||
map_transform(
|
||||
std::move(transform),
|
||||
consumer
|
||||
), [consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
Transform _transform;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Transform>
|
||||
inline auto map(Transform &&transform)
|
||||
-> details::map_helper<std::decay_t<Transform>> {
|
||||
return details::map_helper<std::decay_t<Transform>>(
|
||||
std::forward<Transform>(transform));
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
template <
|
||||
typename Transform,
|
||||
typename Value,
|
||||
typename NewError,
|
||||
typename Handlers>
|
||||
class map_error_transform_helper {
|
||||
public:
|
||||
map_error_transform_helper(
|
||||
Transform &&transform,
|
||||
const consumer<Value, NewError, Handlers> &consumer)
|
||||
: _transform(std::move(transform))
|
||||
, _consumer(consumer) {
|
||||
}
|
||||
template <
|
||||
typename OtherError,
|
||||
typename = std::enable_if_t<
|
||||
std::is_rvalue_reference_v<OtherError&&>>>
|
||||
void operator()(OtherError &&error) const {
|
||||
_consumer.put_error_forward(
|
||||
details::callable_invoke(_transform, std::move(error)));
|
||||
}
|
||||
template <
|
||||
typename OtherError,
|
||||
typename = decltype(
|
||||
std::declval<Transform>()(const_ref_val<OtherError>()))>
|
||||
void operator()(const OtherError &error) const {
|
||||
_consumer.put_error_forward(
|
||||
details::callable_invoke(_transform, error));
|
||||
}
|
||||
|
||||
private:
|
||||
consumer<Value, NewError, Handlers> _consumer;
|
||||
Transform _transform;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename Transform,
|
||||
typename Value,
|
||||
typename NewError,
|
||||
typename Handlers,
|
||||
typename = std::enable_if_t<
|
||||
std::is_rvalue_reference_v<Transform&&>>>
|
||||
inline map_error_transform_helper<Transform, Value, NewError, Handlers>
|
||||
map_error_transform(
|
||||
Transform &&transform,
|
||||
const consumer<Value, NewError, Handlers> &consumer) {
|
||||
return { std::move(transform), consumer };
|
||||
}
|
||||
|
||||
template <typename Transform>
|
||||
class map_error_helper {
|
||||
public:
|
||||
template <typename OtherTransform>
|
||||
map_error_helper(OtherTransform &&transform)
|
||||
: _transform(std::forward<OtherTransform>(transform)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Generator,
|
||||
typename NewError = details::callable_result<
|
||||
Transform,
|
||||
Error>>
|
||||
auto operator()(producer<Value, Error, Generator> &&initial) {
|
||||
return make_producer<Value, NewError>([
|
||||
initial = std::move(initial),
|
||||
transform = std::move(_transform)
|
||||
](const auto &consumer) mutable {
|
||||
return std::move(initial).start(
|
||||
[consumer](auto &&value) {
|
||||
consumer.put_next_forward(
|
||||
std::forward<decltype(value)>(value));
|
||||
}, map_error_transform(
|
||||
std::move(transform),
|
||||
consumer
|
||||
), [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
Transform _transform;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Transform>
|
||||
inline auto map_error(Transform &&transform)
|
||||
-> details::map_error_helper<std::decay_t<Transform>> {
|
||||
return details::map_error_helper<std::decay_t<Transform>>(
|
||||
std::forward<Transform>(transform));
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,474 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
struct base_mapper {
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
constexpr bool is_mapper_v = std::is_base_of_v<
|
||||
base_mapper,
|
||||
std::decay_t<Type>>;
|
||||
|
||||
template <std::size_t Index>
|
||||
struct argument_mapper : base_mapper {
|
||||
template <
|
||||
typename Arg,
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) >= Index)>>
|
||||
static constexpr decltype(auto) call(Arg &&arg, Args &&...args) {
|
||||
return argument_mapper<Index - 1>::call(
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > Index)>>
|
||||
constexpr auto operator()(Args &&...args) const {
|
||||
return call(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
struct argument_mapper<0> : base_mapper {
|
||||
template <
|
||||
typename Arg,
|
||||
typename ...Args>
|
||||
static constexpr decltype(auto) call(Arg &&arg, Args &&...args) {
|
||||
return std::forward<Arg>(arg);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Arg,
|
||||
typename ...Args>
|
||||
constexpr auto operator()(Arg &&arg, Args &&...args) const {
|
||||
return std::forward<Arg>(arg);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class value_mapper : public base_mapper {
|
||||
public:
|
||||
template <typename OtherType>
|
||||
constexpr value_mapper(OtherType &&value)
|
||||
: _value(std::forward<OtherType>(value)) {
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr auto operator()(Args &&...args) const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
private:
|
||||
Type _value;
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct wrap_mapper {
|
||||
using type = std::conditional_t<
|
||||
is_mapper_v<Type>,
|
||||
Type,
|
||||
value_mapper<Type>>;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
using wrap_mapper_t = typename wrap_mapper<Type>::type;
|
||||
|
||||
template <typename Type, typename Operator>
|
||||
class unary_operator_mapper : public base_mapper {
|
||||
using TypeWrapper = wrap_mapper_t<std::decay_t<Type>>;
|
||||
|
||||
public:
|
||||
template <typename OtherType>
|
||||
constexpr unary_operator_mapper(OtherType &&value)
|
||||
: _value(std::forward<OtherType>(value)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename Result = decltype((Operator{})(
|
||||
std::declval<TypeWrapper>()(std::declval<Args>()...)))>
|
||||
constexpr std::decay_t<Result> operator()(Args &&...args) const {
|
||||
return (Operator{})(
|
||||
_value(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
private:
|
||||
TypeWrapper _value;
|
||||
|
||||
};
|
||||
|
||||
template <typename Left, typename Right, typename Operator>
|
||||
class binary_operator_mapper : public base_mapper {
|
||||
using LeftWrapper = wrap_mapper_t<std::decay_t<Left>>;
|
||||
using RightWrapper = wrap_mapper_t<std::decay_t<Right>>;
|
||||
|
||||
public:
|
||||
template <typename OtherLeft, typename OtherRight>
|
||||
constexpr binary_operator_mapper(OtherLeft &&left, OtherRight &&right)
|
||||
: _left(std::forward<OtherLeft>(left))
|
||||
, _right(std::forward<OtherRight>(right)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename Result = decltype((Operator{})(
|
||||
std::declval<LeftWrapper>()(std::declval<Args>()...),
|
||||
std::declval<RightWrapper>()(std::declval<Args>()...)))>
|
||||
constexpr std::decay_t<Result> operator()(Args &&...args) const {
|
||||
return (Operator{})(
|
||||
_left(std::forward<Args>(args)...),
|
||||
_right(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
private:
|
||||
LeftWrapper _left;
|
||||
RightWrapper _right;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator+(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::plus<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator-(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::minus<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator*(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::multiplies<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator/(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::divides<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator%(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::modulus<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Type>
|
||||
>>
|
||||
inline auto operator-(Type &&value) {
|
||||
return unary_operator_mapper<
|
||||
Type,
|
||||
std::negate<>>(
|
||||
std::forward<Type>(value));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator<(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::less<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator<=(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::less_equal<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator>(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::greater<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator>=(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::greater_equal<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator==(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::equal_to<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator!=(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::not_equal_to<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator&&(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::logical_and<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator||(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::logical_or<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Type>
|
||||
>>
|
||||
inline auto operator!(Type &&value) {
|
||||
return unary_operator_mapper<
|
||||
Type,
|
||||
std::logical_not<>>(
|
||||
std::forward<Type>(value));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator&(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::bit_and<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator|(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::bit_or<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Left,
|
||||
typename Right,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Left> || is_mapper_v<Right>
|
||||
>>
|
||||
inline auto operator^(Left &&left, Right &&right) {
|
||||
return binary_operator_mapper<
|
||||
Left,
|
||||
Right,
|
||||
std::bit_xor<>>(
|
||||
std::forward<Left>(left),
|
||||
std::forward<Right>(right));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Type,
|
||||
typename = std::enable_if_t<
|
||||
is_mapper_v<Type>
|
||||
>>
|
||||
inline auto operator~(Type &&value) {
|
||||
return unary_operator_mapper<
|
||||
Type,
|
||||
std::bit_not<>>(
|
||||
std::forward<Type>(value));
|
||||
}
|
||||
|
||||
template <typename ...Mappers>
|
||||
class tuple_mapper {
|
||||
template <typename ...Args>
|
||||
using tuple_result = std::tuple<decltype(
|
||||
std::declval<wrap_mapper_t<std::decay_t<Mappers>>>()(
|
||||
std::declval<Args>()...))...>;
|
||||
public:
|
||||
template <typename ...OtherMappers>
|
||||
tuple_mapper(OtherMappers &&...mappers) : _mappers(
|
||||
std::forward<OtherMappers>(mappers)...) {
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr tuple_result<Args...> operator()(
|
||||
Args &&...args) const {
|
||||
constexpr auto kArity = sizeof...(Mappers);
|
||||
return call_helper(
|
||||
std::make_index_sequence<kArity>(),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename ...Args, std::size_t ...I>
|
||||
inline tuple_result<Args...> call_helper(
|
||||
std::index_sequence<I...>,
|
||||
Args &&...args) const {
|
||||
return std::make_tuple(
|
||||
std::get<I>(_mappers)(std::forward<Args>(args)...)...);
|
||||
}
|
||||
|
||||
std::tuple<wrap_mapper_t<std::decay_t<Mappers>>...> _mappers;
|
||||
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
tuple_mapper<Args...> tuple(Args &&...args) {
|
||||
return tuple_mapper<Args...>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
namespace mappers {
|
||||
|
||||
constexpr const details::argument_mapper<0> _1;
|
||||
constexpr const details::argument_mapper<1> _2;
|
||||
constexpr const details::argument_mapper<2> _3;
|
||||
constexpr const details::argument_mapper<3> _4;
|
||||
constexpr const details::argument_mapper<4> _5;
|
||||
constexpr const details::argument_mapper<5> _6;
|
||||
constexpr const details::argument_mapper<6> _7;
|
||||
constexpr const details::argument_mapper<7> _8;
|
||||
constexpr const details::argument_mapper<8> _9;
|
||||
constexpr const details::argument_mapper<9> _10;
|
||||
|
||||
constexpr const auto _1_of_two = ((void)_2, _1);
|
||||
|
||||
} // namespace mappers
|
||||
} // namespace rpl
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
struct merge_state {
|
||||
merge_state(int working) : working(working) {
|
||||
}
|
||||
int working = 0;
|
||||
};
|
||||
|
||||
template <size_t Index, typename consumer_type>
|
||||
class merge_subscribe_one {
|
||||
public:
|
||||
merge_subscribe_one(
|
||||
const consumer_type &consumer,
|
||||
merge_state *state)
|
||||
: _consumer(consumer)
|
||||
, _state(state) {
|
||||
}
|
||||
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
void subscribe(producer<Value, Error, Generator> &&producer) {
|
||||
_consumer.add_lifetime(std::move(producer).start(
|
||||
[consumer = _consumer](auto &&value) {
|
||||
consumer.put_next_forward(
|
||||
std::forward<decltype(value)>(value));
|
||||
}, [consumer = _consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer = _consumer, state = _state] {
|
||||
if (!--state->working) {
|
||||
consumer.put_done();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private:
|
||||
const consumer_type &_consumer;
|
||||
merge_state *_state = nullptr;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename consumer_type,
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename ...Generators,
|
||||
std::size_t ...I>
|
||||
inline void merge_subscribe(
|
||||
const consumer_type &consumer,
|
||||
merge_state *state,
|
||||
std::index_sequence<I...>,
|
||||
std::tuple<producer<Value, Error, Generators>...> &&saved) {
|
||||
auto consume = { (
|
||||
details::merge_subscribe_one<I, consumer_type>(
|
||||
consumer,
|
||||
state
|
||||
).subscribe(std::get<I>(std::move(saved))), 0)... };
|
||||
(void)consume;
|
||||
}
|
||||
|
||||
template <typename ...Producers>
|
||||
class merge_implementation_helper;
|
||||
|
||||
template <typename ...Producers>
|
||||
merge_implementation_helper<std::decay_t<Producers>...>
|
||||
make_merge_implementation_helper(Producers &&...producers) {
|
||||
return merge_implementation_helper<std::decay_t<Producers>...>(
|
||||
std::forward<Producers>(producers)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename ...Generators>
|
||||
class merge_implementation_helper<producer<Value, Error, Generators>...> {
|
||||
public:
|
||||
merge_implementation_helper(
|
||||
producer<Value, Error, Generators> &&...producers)
|
||||
: _saved(std::make_tuple(std::move(producers)...)) {
|
||||
}
|
||||
|
||||
template <typename Handlers>
|
||||
lifetime operator()(const consumer<Value, Error, Handlers> &consumer) {
|
||||
auto state = consumer.template make_state<
|
||||
details::merge_state>(sizeof...(Generators));
|
||||
constexpr auto kArity = sizeof...(Generators);
|
||||
details::merge_subscribe(
|
||||
consumer,
|
||||
state,
|
||||
std::make_index_sequence<kArity>(),
|
||||
std::move(_saved));
|
||||
|
||||
return lifetime();
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<producer<Value, Error, Generators>...> _saved;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename ...Generators>
|
||||
inline auto merge_implementation(
|
||||
producer<Value, Error, Generators> &&...producers) {
|
||||
return make_producer<Value, Error>(
|
||||
make_merge_implementation_helper(std::move(producers)...));
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
struct merge_producers : std::false_type {
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr bool merge_producers_v
|
||||
= merge_producers<Args...>::value;
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename ...Generators>
|
||||
struct merge_producers<
|
||||
producer<Value, Error, Generators>...>
|
||||
: std::true_type {
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<
|
||||
details::merge_producers_v<Args...>>>
|
||||
inline decltype(auto) merge(Args &&...args) {
|
||||
return details::merge_implementation(std::move(args)...);
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
|
||||
template <typename Value = empty_value, typename Error = no_error>
|
||||
inline auto never() {
|
||||
return make_producer<Value, Error>([](const auto &consumer) {
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,508 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <rpl/rpl.h>
|
||||
#include <string>
|
||||
|
||||
using namespace rpl;
|
||||
|
||||
class OnDestructor {
|
||||
public:
|
||||
OnDestructor(std::function<void()> callback)
|
||||
: _callback(std::move(callback)) {
|
||||
}
|
||||
~OnDestructor() {
|
||||
if (_callback) {
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> _callback;
|
||||
|
||||
};
|
||||
|
||||
class InvokeCounter {
|
||||
public:
|
||||
InvokeCounter(
|
||||
const std::shared_ptr<int> ©Counter,
|
||||
const std::shared_ptr<int> &moveCounter)
|
||||
: _copyCounter(copyCounter)
|
||||
, _moveCounter(moveCounter) {
|
||||
}
|
||||
InvokeCounter(const InvokeCounter &other)
|
||||
: _copyCounter(other._copyCounter)
|
||||
, _moveCounter(other._moveCounter) {
|
||||
if (_copyCounter) {
|
||||
++*_copyCounter;
|
||||
}
|
||||
}
|
||||
InvokeCounter(InvokeCounter &&other)
|
||||
: _copyCounter(details::take(other._copyCounter))
|
||||
, _moveCounter(details::take(other._moveCounter)) {
|
||||
if (_moveCounter) {
|
||||
++*_moveCounter;
|
||||
}
|
||||
}
|
||||
InvokeCounter &operator=(const InvokeCounter &other) {
|
||||
_copyCounter = other._copyCounter;
|
||||
_moveCounter = other._moveCounter;
|
||||
if (_copyCounter) {
|
||||
++*_copyCounter;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
InvokeCounter &operator=(InvokeCounter &&other) {
|
||||
_copyCounter = details::take(other._copyCounter);
|
||||
_moveCounter = details::take(other._moveCounter);
|
||||
if (_moveCounter) {
|
||||
++*_moveCounter;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<int> _copyCounter;
|
||||
std::shared_ptr<int> _moveCounter;
|
||||
|
||||
};
|
||||
|
||||
TEST_CASE("basic operators tests", "[rpl::operators]") {
|
||||
SECTION("single test") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
auto doneGenerated = std::make_shared<bool>(false);
|
||||
auto destroyed = std::make_shared<bool>(false);
|
||||
auto copyCount = std::make_shared<int>(0);
|
||||
auto moveCount = std::make_shared<int>(0);
|
||||
{
|
||||
InvokeCounter counter(copyCount, moveCount);
|
||||
auto destroyCalled = std::make_shared<OnDestructor>([=] {
|
||||
*destroyed = true;
|
||||
});
|
||||
rpl::lifetime lifetime;
|
||||
single(std::move(counter))
|
||||
| start_with_next_error_done([=](InvokeCounter&&) {
|
||||
(void)destroyCalled;
|
||||
++*sum;
|
||||
}, [=](no_error) {
|
||||
(void)destroyCalled;
|
||||
}, [=] {
|
||||
(void)destroyCalled;
|
||||
*doneGenerated = true;
|
||||
}, lifetime);
|
||||
}
|
||||
REQUIRE(*sum == 1);
|
||||
REQUIRE(*doneGenerated);
|
||||
REQUIRE(*destroyed);
|
||||
REQUIRE(*copyCount == 0);
|
||||
}
|
||||
|
||||
SECTION("then test") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
auto doneGenerated = std::make_shared<bool>(false);
|
||||
auto destroyed = std::make_shared<bool>(false);
|
||||
auto copyCount = std::make_shared<int>(0);
|
||||
auto moveCount = std::make_shared<int>(0);
|
||||
{
|
||||
auto testing = complete<InvokeCounter>() | type_erased();
|
||||
for (auto i = 0; i != 5; ++i) {
|
||||
InvokeCounter counter(copyCount, moveCount);
|
||||
testing = std::move(testing)
|
||||
| then(single(std::move(counter)));
|
||||
}
|
||||
auto destroyCalled = std::make_shared<OnDestructor>([=] {
|
||||
*destroyed = true;
|
||||
});
|
||||
|
||||
rpl::lifetime lifetime;
|
||||
std::move(testing)
|
||||
| then(complete<InvokeCounter>())
|
||||
| start_with_next_error_done([=](InvokeCounter&&) {
|
||||
(void)destroyCalled;
|
||||
++*sum;
|
||||
}, [=](no_error) {
|
||||
(void)destroyCalled;
|
||||
}, [=] {
|
||||
(void)destroyCalled;
|
||||
*doneGenerated = true;
|
||||
}, lifetime);
|
||||
}
|
||||
REQUIRE(*sum == 5);
|
||||
REQUIRE(*doneGenerated);
|
||||
REQUIRE(*destroyed);
|
||||
REQUIRE(*copyCount == 0);
|
||||
}
|
||||
|
||||
SECTION("map test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
single(1)
|
||||
| then(single(2))
|
||||
| then(single(3))
|
||||
| then(single(4))
|
||||
| then(single(5))
|
||||
| map([](int value) {
|
||||
return std::to_string(value);
|
||||
})
|
||||
| start_with_next([=](std::string &&value) {
|
||||
*sum += std::move(value) + ' ';
|
||||
}, lifetime);
|
||||
}
|
||||
REQUIRE(*sum == "1 2 3 4 5 ");
|
||||
}
|
||||
|
||||
SECTION("deferred test") {
|
||||
auto launched = std::make_shared<int>(0);
|
||||
auto checked = std::make_shared<int>(0);
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
auto make_next = [=] {
|
||||
return deferred([=] {
|
||||
return single(++*launched);
|
||||
});
|
||||
};
|
||||
make_next()
|
||||
| then(make_next())
|
||||
| then(make_next())
|
||||
| then(make_next())
|
||||
| then(make_next())
|
||||
| start_with_next([=](int value) {
|
||||
REQUIRE(++*checked == *launched);
|
||||
REQUIRE(*checked == value);
|
||||
}, lifetime);
|
||||
REQUIRE(*launched == 5);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("filter test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
single(1)
|
||||
| then(single(1))
|
||||
| then(single(2))
|
||||
| then(single(2))
|
||||
| then(single(3))
|
||||
| filter([](int value) { return value != 2; })
|
||||
| map([](int value) {
|
||||
return std::to_string(value);
|
||||
})
|
||||
| start_with_next([=](std::string &&value) {
|
||||
*sum += std::move(value) + ' ';
|
||||
}, lifetime);
|
||||
}
|
||||
REQUIRE(*sum == "1 1 3 ");
|
||||
}
|
||||
|
||||
SECTION("filter tuple test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
auto lifetime = single(std::make_tuple(1, 2))
|
||||
| then(single(std::make_tuple(1, 2)))
|
||||
| then(single(std::make_tuple(2, 3)))
|
||||
| then(single(std::make_tuple(2, 3)))
|
||||
| then(single(std::make_tuple(3, 4)))
|
||||
| filter([](auto first, auto second) { return first != 2; })
|
||||
| map([](auto first, auto second) {
|
||||
return std::to_string(second);
|
||||
})
|
||||
| start_with_next([=](std::string &&value) {
|
||||
*sum += std::move(value) + ' ';
|
||||
});
|
||||
}
|
||||
REQUIRE(*sum == "2 2 4 ");
|
||||
}
|
||||
|
||||
SECTION("distinct_until_changed test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
single(1)
|
||||
| then(single(1))
|
||||
| then(single(2))
|
||||
| then(single(2))
|
||||
| then(single(3))
|
||||
| distinct_until_changed()
|
||||
| map([](int value) {
|
||||
return std::to_string(value);
|
||||
})
|
||||
| start_with_next([=](std::string &&value) {
|
||||
*sum += std::move(value) + ' ';
|
||||
}, lifetime);
|
||||
}
|
||||
REQUIRE(*sum == "1 2 3 ");
|
||||
}
|
||||
|
||||
SECTION("flatten_latest test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
{
|
||||
event_stream<int> stream;
|
||||
single(single(1) | then(single(2)))
|
||||
| then(single(single(3) | then(single(4))))
|
||||
| then(single(single(5) | then(stream.events())))
|
||||
| flatten_latest()
|
||||
| map([](int value) {
|
||||
return std::to_string(value);
|
||||
})
|
||||
| start_with_next_done([=](std::string &&value) {
|
||||
*sum += std::move(value) + ' ';
|
||||
}, [=] {
|
||||
*sum += "done ";
|
||||
}, lifetime);
|
||||
stream.fire(6);
|
||||
}
|
||||
single(single(1))
|
||||
| then(single(single(2) | then(single(3))))
|
||||
| then(single(single(4) | then(single(5)) | then(single(6))))
|
||||
| flatten_latest()
|
||||
| map([](int value) {
|
||||
return std::to_string(value);
|
||||
})
|
||||
| start_with_next([=](std::string &&value) {
|
||||
*sum += std::move(value) + ' ';
|
||||
}, lifetime);
|
||||
}
|
||||
REQUIRE(*sum == "1 2 3 4 5 6 done 1 2 3 4 5 6 ");
|
||||
}
|
||||
|
||||
SECTION("combine vector test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
event_stream<bool> a;
|
||||
event_stream<bool> b;
|
||||
event_stream<bool> c;
|
||||
|
||||
std::vector<producer<bool>> v;
|
||||
v.push_back(a.events());
|
||||
v.push_back(b.events());
|
||||
v.push_back(c.events());
|
||||
|
||||
combine(std::move(v), [](const auto &values) {
|
||||
return values[0] && values[1] && !values[2];
|
||||
})
|
||||
| start_with_next([=](bool value) {
|
||||
*sum += std::to_string(value ? 1 : 0);
|
||||
}, lifetime);
|
||||
|
||||
a.fire(true);
|
||||
b.fire(true);
|
||||
c.fire(false);
|
||||
a.fire(false);
|
||||
b.fire(true);
|
||||
a.fire(true);
|
||||
c.fire(true);
|
||||
}
|
||||
REQUIRE(*sum == "10010");
|
||||
}
|
||||
|
||||
SECTION("combine test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
event_stream<int> a;
|
||||
event_stream<short> b;
|
||||
event_stream<char> c;
|
||||
|
||||
combine(
|
||||
a.events(),
|
||||
b.events(),
|
||||
c.events(),
|
||||
[](long a, long b, long c) {
|
||||
return a;
|
||||
})
|
||||
| start_with_next([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, lifetime);
|
||||
|
||||
combine(
|
||||
a.events(),
|
||||
b.events(),
|
||||
c.events(),
|
||||
[](auto &&value) {
|
||||
return std::get<1>(value);
|
||||
})
|
||||
| start_with_next([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, lifetime);
|
||||
|
||||
combine(a.events(), b.events(), c.events())
|
||||
| map([](auto &&value) {
|
||||
return std::make_tuple(
|
||||
std::to_string(std::get<0>(value)),
|
||||
std::to_string(std::get<1>(value)),
|
||||
std::to_string(std::get<2>(value)));
|
||||
})
|
||||
| start_with_next([=](auto &&value) {
|
||||
*sum += std::get<0>(value) + ' '
|
||||
+ std::get<1>(value) + ' '
|
||||
+ std::get<2>(value) + ' ';
|
||||
}, lifetime);
|
||||
a.fire(1);
|
||||
b.fire(2);
|
||||
c.fire(3);
|
||||
a.fire(4);
|
||||
b.fire(5);
|
||||
c.fire(6);
|
||||
}
|
||||
REQUIRE(*sum == "121 2 3 424 2 3 454 5 3 454 5 6 ");
|
||||
}
|
||||
|
||||
SECTION("mappers test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
event_stream<int> a;
|
||||
event_stream<short> b;
|
||||
event_stream<char> c;
|
||||
|
||||
using namespace mappers;
|
||||
|
||||
// MSVC BUG + REGRESSION rpl::mappers::tuple :(
|
||||
//combine(
|
||||
// a.events(),
|
||||
// b.events(),
|
||||
// tuple(_1, _1 + _2)
|
||||
//) | rpl::start_with_next([=](int a, int a_plus_b) {
|
||||
// *sum += std::to_string(a * a_plus_b);
|
||||
//}, lifetime);
|
||||
|
||||
combine(
|
||||
a.events(),
|
||||
b.events(),
|
||||
c.events(),
|
||||
_1 + _2 + _3 + 10)
|
||||
| start_with_next([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, lifetime);
|
||||
|
||||
a.fire(1);
|
||||
b.fire(2);
|
||||
c.fire(3);
|
||||
a.fire(4);
|
||||
b.fire(5);
|
||||
c.fire(6);
|
||||
}
|
||||
REQUIRE(*sum == "16192225");
|
||||
|
||||
// MSVC BUG + REGRESSION rpl::mappers::tuple :(
|
||||
//REQUIRE(*sum == "3162419362225");
|
||||
}
|
||||
|
||||
SECTION("after_next test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
ints(3)
|
||||
| after_next([=](int value) {
|
||||
*sum += std::to_string(-value-1);
|
||||
})
|
||||
| start_with_next([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, lifetime);
|
||||
}
|
||||
REQUIRE(*sum == "0-11-22-3");
|
||||
}
|
||||
|
||||
SECTION("combine_previous test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
event_stream<int> a;
|
||||
|
||||
a.events(
|
||||
) | combine_previous(
|
||||
) | start_with_next([=](int previous, int next) {
|
||||
*sum += std::to_string(previous) + ' ';
|
||||
*sum += std::to_string(next) + ' ';
|
||||
}, lifetime);
|
||||
|
||||
a.events(
|
||||
) | combine_previous(
|
||||
5
|
||||
) | start_with_next([=](int previous, int next) {
|
||||
*sum += std::to_string(10 + previous) + ' ';
|
||||
*sum += std::to_string(next) + ' ';
|
||||
}, lifetime);
|
||||
|
||||
a.fire(1);
|
||||
a.fire(2);
|
||||
a.fire(3);
|
||||
a.fire(4);
|
||||
}
|
||||
REQUIRE(*sum == "15 1 1 2 11 2 2 3 12 3 3 4 13 4 ");
|
||||
}
|
||||
|
||||
SECTION("take test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
ints(10) | take(3)
|
||||
| start_with_next_done([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, [=] {
|
||||
*sum += "done";
|
||||
}, lifetime);
|
||||
}
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
ints(3) | take(3)
|
||||
| start_with_next_done([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, [=] {
|
||||
*sum += "done";
|
||||
}, lifetime);
|
||||
}
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
ints(3) | take(10)
|
||||
| start_with_next_done([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, [=] {
|
||||
*sum += "done";
|
||||
}, lifetime);
|
||||
}
|
||||
REQUIRE(*sum == "012done012done012done");
|
||||
}
|
||||
|
||||
SECTION("skip test") {
|
||||
auto sum = std::make_shared<std::string>("");
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
ints(10) | skip(5)
|
||||
| start_with_next_done([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, [=] {
|
||||
*sum += "done";
|
||||
}, lifetime);
|
||||
}
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
ints(3) | skip(3)
|
||||
| start_with_next_done([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, [=] {
|
||||
*sum += "done";
|
||||
}, lifetime);
|
||||
}
|
||||
{
|
||||
rpl::lifetime lifetime;
|
||||
ints(3) | skip(10)
|
||||
| start_with_next_done([=](int value) {
|
||||
*sum += std::to_string(value);
|
||||
}, [=] {
|
||||
*sum += "done";
|
||||
}, lifetime);
|
||||
}
|
||||
REQUIRE(*sum == "56789donedonedone");
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,441 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <rpl/producer.h>
|
||||
#include <rpl/event_stream.h>
|
||||
|
||||
using namespace rpl;
|
||||
|
||||
class OnDestructor {
|
||||
public:
|
||||
OnDestructor(std::function<void()> callback)
|
||||
: _callback(std::move(callback)) {
|
||||
}
|
||||
~OnDestructor() {
|
||||
if (_callback) {
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> _callback;
|
||||
|
||||
};
|
||||
|
||||
TEST_CASE("basic producer tests", "[rpl::producer]") {
|
||||
SECTION("producer next, done and lifetime end test") {
|
||||
auto lifetimeEnded = std::make_shared<bool>(false);
|
||||
auto sum = std::make_shared<int>(0);
|
||||
auto doneGenerated = std::make_shared<bool>(false);
|
||||
auto destroyed = std::make_shared<bool>(false);
|
||||
{
|
||||
auto destroyCaller = std::make_shared<OnDestructor>([=] {
|
||||
*destroyed = true;
|
||||
});
|
||||
{
|
||||
auto alive = make_producer<int>([=](auto &&consumer) {
|
||||
(void)destroyCaller;
|
||||
consumer.put_next(1);
|
||||
consumer.put_next(2);
|
||||
consumer.put_next(3);
|
||||
consumer.put_done();
|
||||
return [=] {
|
||||
(void)destroyCaller;
|
||||
*lifetimeEnded = true;
|
||||
};
|
||||
}).start([=](int value) {
|
||||
(void)destroyCaller;
|
||||
*sum += value;
|
||||
}, [=](no_error) {
|
||||
(void)destroyCaller;
|
||||
}, [=]() {
|
||||
(void)destroyCaller;
|
||||
*doneGenerated = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
REQUIRE(*sum == 1 + 2 + 3);
|
||||
REQUIRE(*doneGenerated);
|
||||
REQUIRE(*lifetimeEnded);
|
||||
REQUIRE(*destroyed);
|
||||
}
|
||||
|
||||
SECTION("producer error test") {
|
||||
auto errorGenerated = std::make_shared<bool>(false);
|
||||
{
|
||||
auto alive = make_producer<no_value, bool>([=](auto &&consumer) {
|
||||
consumer.put_error(true);
|
||||
return lifetime();
|
||||
}).start([=](no_value) {
|
||||
}, [=](bool error) {
|
||||
*errorGenerated = error;
|
||||
}, [=]() {
|
||||
});
|
||||
}
|
||||
REQUIRE(*errorGenerated);
|
||||
}
|
||||
|
||||
SECTION("nested lifetimes test") {
|
||||
auto lifetimeEndCount = std::make_shared<int>(0);
|
||||
{
|
||||
auto lifetimes = lifetime();
|
||||
{
|
||||
auto testProducer = make_producer<no_value>([=](auto &&consumer) {
|
||||
return [=] {
|
||||
++*lifetimeEndCount;
|
||||
};
|
||||
});
|
||||
testProducer.start_copy([=](no_value) {
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, lifetimes);
|
||||
std::move(testProducer).start([=](no_value) {
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, lifetimes);
|
||||
}
|
||||
REQUIRE(*lifetimeEndCount == 0);
|
||||
}
|
||||
REQUIRE(*lifetimeEndCount == 2);
|
||||
}
|
||||
|
||||
SECTION("nested producers test") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
auto lifetimeEndCount = std::make_shared<int>(0);
|
||||
auto saved = lifetime();
|
||||
{
|
||||
make_producer<int>([=](auto &&consumer) {
|
||||
auto inner = make_producer<int>([=](auto &&consumer) {
|
||||
consumer.put_next(1);
|
||||
consumer.put_next(2);
|
||||
consumer.put_next(3);
|
||||
return [=] {
|
||||
++*lifetimeEndCount;
|
||||
};
|
||||
});
|
||||
auto result = lifetime([=] {
|
||||
++*lifetimeEndCount;
|
||||
});
|
||||
inner.start_copy([=](int value) {
|
||||
consumer.put_next_copy(value);
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, result);
|
||||
std::move(inner).start([=](int value) {
|
||||
consumer.put_next_copy(value);
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, result);
|
||||
return result;
|
||||
}).start([=](int value) {
|
||||
*sum += value;
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, saved);
|
||||
}
|
||||
REQUIRE(*sum == 1 + 2 + 3 + 1 + 2 + 3);
|
||||
REQUIRE(*lifetimeEndCount == 0);
|
||||
saved.destroy();
|
||||
REQUIRE(*lifetimeEndCount == 3);
|
||||
}
|
||||
|
||||
SECTION("tuple producer test") {
|
||||
auto result = std::make_shared<int>(0);
|
||||
{
|
||||
auto alive = make_producer<std::tuple<int, double>>([=](
|
||||
auto &&consumer) {
|
||||
consumer.put_next(std::make_tuple(1, 2.));
|
||||
return lifetime();
|
||||
}).start([=](int a, double b) {
|
||||
*result = a + int(b);
|
||||
}, [=](no_error error) {
|
||||
}, [=]() {
|
||||
});
|
||||
}
|
||||
REQUIRE(*result == 3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("basic event_streams tests", "[rpl::event_stream]") {
|
||||
SECTION("event_stream basic test") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
event_stream<int> stream;
|
||||
stream.fire(1);
|
||||
stream.fire(2);
|
||||
stream.fire(3);
|
||||
{
|
||||
auto saved = lifetime();
|
||||
stream.events().start([=](int value) {
|
||||
*sum += value;
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, saved);
|
||||
stream.fire(11);
|
||||
stream.fire(12);
|
||||
stream.fire(13);
|
||||
}
|
||||
stream.fire(21);
|
||||
stream.fire(22);
|
||||
stream.fire(23);
|
||||
|
||||
REQUIRE(11 + 12 + 13);
|
||||
}
|
||||
|
||||
SECTION("event_stream add in handler test") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
event_stream<int> stream;
|
||||
|
||||
{
|
||||
auto composite = lifetime();
|
||||
stream.events().start([=, &stream, &composite](int value) {
|
||||
*sum += value;
|
||||
stream.events().start([=](int value) {
|
||||
*sum += value;
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, composite);
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, composite);
|
||||
|
||||
{
|
||||
auto inner = lifetime();
|
||||
stream.events().start([=, &stream, &inner](int value) {
|
||||
*sum += value;
|
||||
stream.events().start([=](int value) {
|
||||
*sum += value;
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, inner);
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, inner);
|
||||
|
||||
stream.fire(1);
|
||||
stream.fire(2);
|
||||
stream.fire(3);
|
||||
}
|
||||
stream.fire(11);
|
||||
stream.fire(12);
|
||||
stream.fire(13);
|
||||
}
|
||||
stream.fire(21);
|
||||
stream.fire(22);
|
||||
stream.fire(23);
|
||||
|
||||
REQUIRE(*sum ==
|
||||
(1 + 1) +
|
||||
((2 + 2) + (2 + 2)) +
|
||||
((3 + 3 + 3) + (3 + 3 + 3)) +
|
||||
(11 + 11 + 11 + 11) +
|
||||
(12 + 12 + 12 + 12 + 12) +
|
||||
(13 + 13 + 13 + 13 + 13 + 13));
|
||||
}
|
||||
|
||||
SECTION("event_stream add and remove in handler test") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
event_stream<int> stream;
|
||||
|
||||
{
|
||||
auto composite = lifetime();
|
||||
stream.events().start([=, &stream, &composite](int value) {
|
||||
*sum += value;
|
||||
composite.destroy();
|
||||
stream.events().start([=](int value) {
|
||||
*sum += value;
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, composite);
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, composite);
|
||||
|
||||
{
|
||||
auto inner = lifetime();
|
||||
stream.events().start([=, &stream, &inner](int value) {
|
||||
*sum += value;
|
||||
inner.destroy();
|
||||
stream.events().start([=](int value) {
|
||||
*sum += value;
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, inner);
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, inner);
|
||||
|
||||
stream.fire(1);
|
||||
stream.fire(2);
|
||||
stream.fire(3);
|
||||
}
|
||||
stream.fire(11);
|
||||
stream.fire(12);
|
||||
stream.fire(13);
|
||||
}
|
||||
stream.fire(21);
|
||||
stream.fire(22);
|
||||
stream.fire(23);
|
||||
|
||||
REQUIRE(*sum ==
|
||||
(1 + 1) +
|
||||
(2 + 2) +
|
||||
(3 + 3) +
|
||||
(11) +
|
||||
(12) +
|
||||
(13));
|
||||
}
|
||||
|
||||
SECTION("event_stream ends before handler lifetime") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
lifetime extended;
|
||||
{
|
||||
event_stream<int> stream;
|
||||
stream.events().start([=](int value) {
|
||||
*sum += value;
|
||||
}, [=](no_error) {
|
||||
}, [=] {
|
||||
}, extended);
|
||||
stream.fire(1);
|
||||
stream.fire(2);
|
||||
stream.fire(3);
|
||||
}
|
||||
REQUIRE(*sum == 1 + 2 + 3);
|
||||
}
|
||||
|
||||
SECTION("event_stream move test") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
lifetime extended;
|
||||
{
|
||||
event_stream<int> stream;
|
||||
stream.events()
|
||||
| start_with_next([=](int value) {
|
||||
*sum += value;
|
||||
}, extended);
|
||||
|
||||
stream.fire(1);
|
||||
stream.fire(2);
|
||||
|
||||
auto movedStream = std::move(stream);
|
||||
movedStream.fire(3);
|
||||
movedStream.fire(4);
|
||||
}
|
||||
REQUIRE(*sum == 1 + 2 + 3 + 4);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("basic piping tests", "[rpl::producer]") {
|
||||
|
||||
SECTION("start_with_*") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
auto dones = std::make_shared<int>(0);
|
||||
{
|
||||
auto alive = lifetime();
|
||||
make_producer<int, int>([=](auto &&consumer) {
|
||||
consumer.put_next(1);
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
}) | start_with_next([=](int value) {
|
||||
*sum += value;
|
||||
}, alive);
|
||||
|
||||
make_producer<int, int>([=](auto &&consumer) {
|
||||
consumer.put_next(11);
|
||||
consumer.put_error(111);
|
||||
return lifetime();
|
||||
}) | start_with_error([=](int value) {
|
||||
*sum += value;
|
||||
}, alive);
|
||||
|
||||
make_producer<int, int>([=](auto &&consumer) {
|
||||
consumer.put_next(1111);
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
}) | start_with_done([=]() {
|
||||
*dones += 1;
|
||||
}, alive);
|
||||
|
||||
make_producer<int, int>([=](auto &&consumer) {
|
||||
consumer.put_next(11111);
|
||||
consumer.put_next(11112);
|
||||
consumer.put_next(11113);
|
||||
consumer.put_error(11114);
|
||||
return lifetime();
|
||||
}) | start_with_next_error([=](int value) {
|
||||
*sum += value;
|
||||
}, [=](int value) {
|
||||
*sum += value;
|
||||
}, alive);
|
||||
}
|
||||
|
||||
auto alive = lifetime();
|
||||
make_producer<int, int>([=](auto &&consumer) {
|
||||
consumer.put_next(111111);
|
||||
consumer.put_next(111112);
|
||||
consumer.put_next(111113);
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
}) | start_with_next_done([=](int value) {
|
||||
*sum += value;
|
||||
}, [=]() {
|
||||
*dones += 11;
|
||||
}, alive);
|
||||
|
||||
make_producer<int, int>([=](auto &&consumer) {
|
||||
consumer.put_error(1111111);
|
||||
return lifetime();
|
||||
}) | start_with_error_done([=](int value) {
|
||||
*sum += value;
|
||||
}, [=]() {
|
||||
*dones = 0;
|
||||
}, alive);
|
||||
|
||||
make_producer<int, int>([=](auto &&consumer) {
|
||||
consumer.put_next(11111111);
|
||||
consumer.put_next(11111112);
|
||||
consumer.put_next(11111113);
|
||||
consumer.put_error(11111114);
|
||||
return lifetime();
|
||||
}) | start_with_next_error_done([=](int value) {
|
||||
*sum += value;
|
||||
}, [=](int value) {
|
||||
*sum += value;
|
||||
}, [=]() {
|
||||
*dones = 0;
|
||||
}, alive);
|
||||
|
||||
REQUIRE(*sum ==
|
||||
1 +
|
||||
111 +
|
||||
11111 + 11112 + 11113 + 11114 +
|
||||
111111 + 111112 + 111113 +
|
||||
1111111 +
|
||||
11111111 + 11111112 + 11111113 + 11111114);
|
||||
REQUIRE(*dones == 1 + 11);
|
||||
}
|
||||
|
||||
SECTION("start_with_next should copy its callback") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
{
|
||||
auto next = [=](int value) {
|
||||
REQUIRE(sum != nullptr);
|
||||
*sum += value;
|
||||
};
|
||||
|
||||
for (int i = 0; i != 3; ++i) {
|
||||
auto alive = lifetime();
|
||||
make_producer<int, int>([=](auto &&consumer) {
|
||||
consumer.put_next(1);
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
}) | start_with_next(next, alive);
|
||||
}
|
||||
}
|
||||
REQUIRE(*sum == 3);
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
#include <vector>
|
||||
|
||||
namespace rpl {
|
||||
|
||||
template <typename Value, typename Error = no_error>
|
||||
inline auto single(Value &&value) {
|
||||
return make_producer<std::decay_t<Value>, Error>([
|
||||
value = std::forward<Value>(value)
|
||||
](const auto &consumer) mutable {
|
||||
consumer.put_next(std::move(value));
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Error = no_error>
|
||||
inline auto single() {
|
||||
return make_producer<rpl::empty_value, Error>([](const auto &consumer) {
|
||||
consumer.put_next({});
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Value, typename Error = no_error>
|
||||
inline auto vector(std::vector<Value> &&values) {
|
||||
return make_producer<Value, Error>([
|
||||
values = std::move(values)
|
||||
](const auto &consumer) mutable {
|
||||
for (auto &value : values) {
|
||||
consumer.put_next(std::move(value));
|
||||
}
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Error = no_error>
|
||||
inline auto vector(std::vector<bool> &&values) {
|
||||
return make_producer<bool, Error>([
|
||||
values = std::move(values)
|
||||
](const auto &consumer) {
|
||||
for (auto value : values) {
|
||||
consumer.put_next_copy(value);
|
||||
}
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
|
||||
template <
|
||||
typename Range,
|
||||
typename Value = std::decay_t<
|
||||
decltype(*std::begin(std::declval<Range>()))>>
|
||||
inline auto range(Range &&range) {
|
||||
return vector(std::vector<Value>(
|
||||
std::begin(range),
|
||||
std::end(range)));
|
||||
}
|
||||
|
||||
inline auto ints(int from, int till) {
|
||||
Expects(from <= till);
|
||||
return make_producer<int>([from, till](const auto &consumer) {
|
||||
for (auto i = from; i != till; ++i) {
|
||||
consumer.put_next_copy(i);
|
||||
}
|
||||
consumer.put_done();
|
||||
return lifetime();
|
||||
});
|
||||
}
|
||||
|
||||
inline auto ints(int count) {
|
||||
return ints(0, count);
|
||||
}
|
||||
|
||||
} // namespace rpl
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// rpl - reactive programming library
|
||||
|
||||
#include <rpl/lifetime.h>
|
||||
#include <rpl/consumer.h>
|
||||
#include <rpl/producer.h>
|
||||
#include <rpl/event_stream.h>
|
||||
|
||||
#include <rpl/range.h>
|
||||
#include <rpl/complete.h>
|
||||
#include <rpl/fail.h>
|
||||
#include <rpl/never.h>
|
||||
|
||||
#include <rpl/take.h>
|
||||
#include <rpl/skip.h>
|
||||
#include <rpl/then.h>
|
||||
#include <rpl/deferred.h>
|
||||
#include <rpl/map.h>
|
||||
#include <rpl/mappers.h>
|
||||
#include <rpl/merge.h>
|
||||
#include <rpl/filter.h>
|
||||
#include <rpl/distinct_until_changed.h>
|
||||
#include <rpl/type_erased.h>
|
||||
#include <rpl/flatten_latest.h>
|
||||
#include <rpl/combine.h>
|
||||
#include <rpl/combine_previous.h>
|
||||
#include <rpl/conditional.h>
|
||||
#include <rpl/variable.h>
|
||||
|
||||
#include <rpl/before_next.h>
|
||||
#include <rpl/after_next.h>
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
class skip_helper {
|
||||
public:
|
||||
skip_helper(int count) : _count(count) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Generator>
|
||||
auto operator()(producer<Value, Error, Generator> &&initial) {
|
||||
return make_producer<Value, Error>([
|
||||
initial = std::move(initial),
|
||||
skipping = _count
|
||||
](const auto &consumer) mutable {
|
||||
auto count = consumer.template make_state<int>(skipping);
|
||||
auto initial_consumer = make_consumer<Value, Error>(
|
||||
[consumer, count](auto &&value) {
|
||||
if (*count) {
|
||||
--*count;
|
||||
} else {
|
||||
consumer.put_next_forward(
|
||||
std::forward<decltype(value)>(value));
|
||||
}
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
consumer.add_lifetime(initial_consumer.terminator());
|
||||
return std::move(initial).start_existing(initial_consumer);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
int _count = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline auto skip(int count)
|
||||
-> details::skip_helper {
|
||||
Expects(count >= 0);
|
||||
|
||||
return details::skip_helper(count);
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
class take_helper {
|
||||
public:
|
||||
take_helper(int count) : _count(count) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Error,
|
||||
typename Generator>
|
||||
auto operator()(producer<Value, Error, Generator> &&initial) {
|
||||
return make_producer<Value, Error>([
|
||||
initial = std::move(initial),
|
||||
limit = _count
|
||||
](const auto &consumer) mutable {
|
||||
auto count = consumer.template make_state<int>(limit);
|
||||
auto initial_consumer = make_consumer<Value, Error>(
|
||||
[consumer, count](auto &&value) {
|
||||
auto left = (*count)--;
|
||||
if (left) {
|
||||
consumer.put_next_forward(
|
||||
std::forward<decltype(value)>(value));
|
||||
--left;
|
||||
}
|
||||
if (!left) {
|
||||
consumer.put_done();
|
||||
}
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
});
|
||||
consumer.add_lifetime(initial_consumer.terminator());
|
||||
return std::move(initial).start_existing(initial_consumer);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
int _count = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline auto take(int count)
|
||||
-> details::take_helper {
|
||||
Expects(count >= 0);
|
||||
|
||||
return details::take_helper(count);
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
class then_helper {
|
||||
public:
|
||||
then_helper(producer<Value, Error, Generator> &&following)
|
||||
: _following(std::move(following)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherValue,
|
||||
typename OtherError,
|
||||
typename OtherGenerator,
|
||||
typename NewValue = superset_type_t<Value, OtherValue>,
|
||||
typename NewError = superset_type_t<Error, OtherError>>
|
||||
auto operator()(
|
||||
producer<OtherValue, OtherError, OtherGenerator> &&initial
|
||||
) {
|
||||
return make_producer<NewValue, NewError>([
|
||||
initial = std::move(initial),
|
||||
following = std::move(_following)
|
||||
](const auto &consumer) mutable {
|
||||
return std::move(initial).start(
|
||||
[consumer](auto &&value) {
|
||||
consumer.put_next_forward(
|
||||
std::forward<decltype(value)>(value));
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [
|
||||
consumer,
|
||||
following = std::move(following)
|
||||
]() mutable {
|
||||
consumer.add_lifetime(std::move(following).start(
|
||||
[consumer](auto &&value) {
|
||||
consumer.put_next_forward(
|
||||
std::forward<decltype(value)>(value));
|
||||
}, [consumer](auto &&error) {
|
||||
consumer.put_error_forward(
|
||||
std::forward<decltype(error)>(error));
|
||||
}, [consumer] {
|
||||
consumer.put_done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
producer<Value, Error, Generator> _following;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
inline auto then(producer<Value, Error, Generator> &&following)
|
||||
-> details::then_helper<Value, Error, Generator> {
|
||||
return { std::move(following) };
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
class type_erased_helper {
|
||||
public:
|
||||
template <typename Value, typename Error, typename Generator>
|
||||
producer<Value, Error> operator()(
|
||||
producer<Value, Error, Generator> &&initial) const {
|
||||
return std::move(initial);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline auto type_erased()
|
||||
-> details::type_erased_helper {
|
||||
return details::type_erased_helper();
|
||||
}
|
||||
|
||||
} // namespace rpl
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
#include <rpl/event_stream.h>
|
||||
|
||||
namespace mapbox {
|
||||
namespace util {
|
||||
|
||||
template <typename ...Types>
|
||||
class variant;
|
||||
|
||||
} // namespace util
|
||||
} // namespace mapbox
|
||||
|
||||
namespace rpl {
|
||||
namespace details {
|
||||
|
||||
template <typename A, typename B>
|
||||
struct supports_equality_compare {
|
||||
template <typename U, typename V>
|
||||
static auto test(const U *u, const V *v)
|
||||
-> decltype(*u == *v, true_t());
|
||||
static false_t test(...);
|
||||
static constexpr bool value
|
||||
= (sizeof(test((const A*)nullptr, (const B*)nullptr))
|
||||
== sizeof(true_t));
|
||||
};
|
||||
|
||||
// Fix for MSVC expression SFINAE.
|
||||
// It still doesn't work! :(
|
||||
//
|
||||
//template <typename Type1, typename ...Types1>
|
||||
//struct supports_equality_compare<
|
||||
// mapbox::util::variant<Type1, Types1...>,
|
||||
// mapbox::util::variant<Type1, Types1...>> {
|
||||
// static constexpr bool value
|
||||
// = (supports_equality_compare<Type1, Type1>::value
|
||||
// && supports_equality_compare<
|
||||
// mapbox::util::variant<Types1...>,
|
||||
// mapbox::util::variant<Types1...>>::value);
|
||||
//
|
||||
//};
|
||||
//template <typename Type>
|
||||
//struct supports_equality_compare<
|
||||
// mapbox::util::variant<Type>,
|
||||
// mapbox::util::variant<Type>> {
|
||||
// static constexpr bool value = supports_equality_compare<Type, Type>::value;
|
||||
//
|
||||
//};
|
||||
|
||||
template <typename A, typename B>
|
||||
constexpr bool supports_equality_compare_v
|
||||
= supports_equality_compare<std::decay_t<A>, std::decay_t<B>>::value;
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Type, typename Error = no_error>
|
||||
class variable final {
|
||||
public:
|
||||
variable() : _data{} {
|
||||
}
|
||||
variable(variable &&other) : _data(std::move(other._data)) {
|
||||
}
|
||||
variable &operator=(variable &&other) {
|
||||
return (*this = std::move(other._data));
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
std::is_constructible_v<Type, OtherType&&>>>
|
||||
variable(OtherType &&data) : _data(std::forward<OtherType>(data)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
std::is_assignable_v<Type&, OtherType&&>>>
|
||||
variable &operator=(OtherType &&data) {
|
||||
_lifetime.destroy();
|
||||
return assign(std::forward<OtherType>(data));
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename OtherError,
|
||||
typename Generator,
|
||||
typename = std::enable_if_t<
|
||||
std::is_assignable_v<Type&, OtherType>>>
|
||||
variable(producer<OtherType, OtherError, Generator> &&stream) {
|
||||
std::move(stream)
|
||||
| start_with_next([=](auto &&data) {
|
||||
assign(std::forward<decltype(data)>(data));
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename OtherError,
|
||||
typename Generator,
|
||||
typename = std::enable_if_t<
|
||||
std::is_assignable_v<Type&, OtherType>>>
|
||||
variable &operator=(
|
||||
producer<OtherType, OtherError, Generator> &&stream) {
|
||||
_lifetime.destroy();
|
||||
std::move(stream)
|
||||
| start_with_next([=](auto &&data) {
|
||||
assign(std::forward<decltype(data)>(data));
|
||||
}, _lifetime);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Type current() const {
|
||||
return _data;
|
||||
}
|
||||
auto value() const {
|
||||
return _changes.events_starting_with_copy(_data);
|
||||
}
|
||||
auto changes() const {
|
||||
return _changes.events();
|
||||
}
|
||||
|
||||
// Send 'done' to all subscribers and unsubscribe them.
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
std::is_assignable_v<Type&, OtherType>>>
|
||||
void reset(OtherType &&data) {
|
||||
_data = std::forward<OtherType>(data);
|
||||
_changes = event_stream<Type, Error>();
|
||||
}
|
||||
void reset() {
|
||||
reset(Type());
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherError,
|
||||
typename = std::enable_if_t<
|
||||
std::is_constructible_v<Error, OtherError&&>>>
|
||||
void reset_with_error(OtherError &&error) {
|
||||
_changes.fire_error(std::forward<OtherError>(error));
|
||||
}
|
||||
void reset_with_error() {
|
||||
reset_with_error(Error());
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename OtherType>
|
||||
variable &assign(OtherType &&data) {
|
||||
if constexpr (details::supports_equality_compare_v<Type, OtherType>) {
|
||||
if (!(_data == data)) {
|
||||
_data = std::forward<OtherType>(data);
|
||||
_changes.fire_copy(_data);
|
||||
}
|
||||
} else if constexpr (details::supports_equality_compare_v<Type, Type>) {
|
||||
auto old = std::move(_data);
|
||||
_data = std::forward<OtherType>(data);
|
||||
if (!(_data == old)) {
|
||||
_changes.fire_copy(_data);
|
||||
}
|
||||
} else {
|
||||
_data = std::forward<OtherType>(data);
|
||||
_changes.fire_copy(_data);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Type _data;
|
||||
event_stream<Type, Error> _changes;
|
||||
lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace rpl
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <rpl/rpl.h>
|
||||
#include <string>
|
||||
|
||||
using namespace rpl;
|
||||
|
||||
TEST_CASE("basic variable tests", "[rpl::variable]") {
|
||||
SECTION("simple test") {
|
||||
auto sum = std::make_shared<int>(0);
|
||||
{
|
||||
auto var = variable<int>(1);
|
||||
auto lifeftime = var.value()
|
||||
| start_with_next([=](int value) {
|
||||
*sum += value;
|
||||
});
|
||||
var = 1;
|
||||
var = 11;
|
||||
var = 111;
|
||||
var = 111;
|
||||
}
|
||||
REQUIRE(*sum == 1 + 11 + 111);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
{
|
||||
'includes': [
|
||||
'common/common.gypi',
|
||||
'../ThirdParty/gyp_helpers/common/common.gypi',
|
||||
'telegram/telegram.gypi',
|
||||
],
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
{
|
||||
'includes': [
|
||||
'common/common.gypi',
|
||||
'../ThirdParty/gyp_helpers/common/common.gypi',
|
||||
],
|
||||
'targets': [{
|
||||
'target_name': 'codegen_lang',
|
||||
|
@ -15,8 +15,8 @@
|
|||
'mac_target': '10.10',
|
||||
},
|
||||
'includes': [
|
||||
'common/executable.gypi',
|
||||
'modules/qt.gypi',
|
||||
'../ThirdParty/gyp_helpers/common/executable.gypi',
|
||||
'../ThirdParty/gyp_helpers/modules/qt.gypi',
|
||||
],
|
||||
|
||||
'include_dirs': [
|
||||
|
@ -52,11 +52,11 @@
|
|||
'mac_target': '10.10',
|
||||
},
|
||||
'includes': [
|
||||
'common/executable.gypi',
|
||||
'modules/qt.gypi',
|
||||
'../ThirdParty/gyp_helpers/common/executable.gypi',
|
||||
'../ThirdParty/gyp_helpers/modules/qt.gypi',
|
||||
],
|
||||
'dependencies': [
|
||||
'lib_base.gyp:lib_base',
|
||||
'../ThirdParty/lib_base/lib_base.gyp:lib_base',
|
||||
],
|
||||
'include_dirs': [
|
||||
'<(src_loc)',
|
||||
|
@ -95,8 +95,8 @@
|
|||
'mac_target': '10.10',
|
||||
},
|
||||
'includes': [
|
||||
'common/executable.gypi',
|
||||
'modules/qt.gypi',
|
||||
'../ThirdParty/gyp_helpers/common/executable.gypi',
|
||||
'../ThirdParty/gyp_helpers/modules/qt.gypi',
|
||||
],
|
||||
|
||||
'include_dirs': [
|
||||
|
@ -132,8 +132,8 @@
|
|||
'mac_target': '10.10',
|
||||
},
|
||||
'includes': [
|
||||
'common/executable.gypi',
|
||||
'modules/qt.gypi',
|
||||
'../ThirdParty/gyp_helpers/common/executable.gypi',
|
||||
'../ThirdParty/gyp_helpers/modules/qt.gypi',
|
||||
],
|
||||
|
||||
'include_dirs': [
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
# This file is part of Telegram Desktop,
|
||||
# the official desktop application for the Telegram messaging service.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'win.gypi',
|
||||
'mac.gypi',
|
||||
'linux.gypi',
|
||||
],
|
||||
'variables': {
|
||||
'variables': {
|
||||
'variables': {
|
||||
'variables': {
|
||||
'variables': {
|
||||
'build_os%': '<(OS)',
|
||||
},
|
||||
'build_os%': '<(build_os)',
|
||||
'conditions': [
|
||||
[ 'build_os == "win"', {
|
||||
'build_win': 1,
|
||||
}, {
|
||||
'build_win': 0,
|
||||
}],
|
||||
[ 'build_os == "mac"', {
|
||||
'build_mac': 1,
|
||||
}, {
|
||||
'build_mac': 0,
|
||||
}],
|
||||
[ 'build_os == "linux"', {
|
||||
'build_linux': 1,
|
||||
}, {
|
||||
'build_linux': 0,
|
||||
}],
|
||||
],
|
||||
},
|
||||
'build_os%': '<(build_os)',
|
||||
'build_win%': '<(build_win)',
|
||||
'build_mac%': '<(build_mac)',
|
||||
'build_linux%': '<(build_linux)',
|
||||
},
|
||||
'build_os%': '<(build_os)',
|
||||
'build_win%': '<(build_win)',
|
||||
'build_mac%': '<(build_mac)',
|
||||
'build_linux%': '<(build_linux)',
|
||||
|
||||
'official_build_target%': '',
|
||||
'build_standard_win%': 'c++17',
|
||||
'libs_loc%': '<(DEPTH)/../../../Libraries',
|
||||
},
|
||||
'build_os%': '<(build_os)',
|
||||
'build_win%': '<(build_win)',
|
||||
'build_mac%': '<(build_mac)',
|
||||
'build_linux%': '<(build_linux)',
|
||||
'official_build_target%': '<(official_build_target)',
|
||||
'build_standard_win%': '<(build_standard_win)',
|
||||
'libs_loc%': '<(libs_loc)',
|
||||
|
||||
# GYP does not support per-configuration libraries :(
|
||||
# So they will be emulated through additional link flags,
|
||||
# which will contain <(ld_lib_prefix)LibraryName<(ld_lib_postfix)
|
||||
'conditions': [
|
||||
[ 'build_win', {
|
||||
'ld_lib_prefix': '',
|
||||
'ld_lib_postfix': '.lib',
|
||||
'exe_ext': '.exe',
|
||||
}, {
|
||||
'ld_lib_prefix': '-l',
|
||||
'ld_lib_postfix': '',
|
||||
'exe_ext': '',
|
||||
}],
|
||||
[ '"<(official_build_target)" == "mac32"', {
|
||||
'mac_target%': '10.6',
|
||||
'build_macold': 1,
|
||||
}, {
|
||||
'mac_target%': '10.8',
|
||||
'build_macold': 0,
|
||||
}],
|
||||
[ '"<(official_build_target)" == "macstore"', {
|
||||
'build_macstore': 1,
|
||||
}, {
|
||||
'build_macstore': 0,
|
||||
}],
|
||||
[ '"<(official_build_target)" == "uwp"', {
|
||||
'build_uwp': 1,
|
||||
}, {
|
||||
'build_uwp': 0,
|
||||
}],
|
||||
],
|
||||
'ld_lib_prefix': '<(ld_lib_prefix)',
|
||||
'ld_lib_postfix': '<(ld_lib_postfix)',
|
||||
'exe_ext': '<(exe_ext)',
|
||||
|
||||
'library%': 'static_library',
|
||||
|
||||
},
|
||||
|
||||
'defines': [
|
||||
'NOMINMAX'
|
||||
],
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
'defines': [
|
||||
'_DEBUG',
|
||||
],
|
||||
},
|
||||
'Release': {
|
||||
'defines': [
|
||||
'NDEBUG',
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
# This file is part of Telegram Desktop,
|
||||
# the official desktop application for the Telegram messaging service.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
{
|
||||
'type': 'executable',
|
||||
'variables': {
|
||||
'win_subsystem': '2', # Windows application
|
||||
},
|
||||
'includes': [
|
||||
'common.gypi',
|
||||
],
|
||||
'msvs_settings': {
|
||||
'VCLinkerTool': {
|
||||
'SubSystem': '<(win_subsystem)',
|
||||
'ImportLibrary': '<(PRODUCT_DIR)/<(_target_name).lib',
|
||||
},
|
||||
},
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
# This file is part of Telegram Desktop,
|
||||
# the official desktop application for the Telegram messaging service.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
{
|
||||
'type': 'static_library',
|
||||
'includes': [
|
||||
'common.gypi',
|
||||
],
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
# This file is part of Telegram Desktop,
|
||||
# the official desktop application for the Telegram messaging service.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
{
|
||||
'conditions': [
|
||||
[ 'build_linux', {
|
||||
'variables': {
|
||||
'linux_common_flags': [
|
||||
'-pipe',
|
||||
'-Wall',
|
||||
'-Werror',
|
||||
'-W',
|
||||
'-fPIC',
|
||||
'-Wno-unused-variable',
|
||||
'-Wno-unused-parameter',
|
||||
'-Wno-unused-function',
|
||||
'-Wno-switch',
|
||||
'-Wno-comment',
|
||||
'-Wno-unused-but-set-variable',
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-sign-compare',
|
||||
'-Wno-attributes',
|
||||
'-Wno-error=class-memaccess',
|
||||
'-Wno-error=parentheses',
|
||||
],
|
||||
'linux_path_ffmpeg%': '/usr/local',
|
||||
'linux_path_openal%': '/usr/local',
|
||||
'linux_path_va%': '/usr/local',
|
||||
'linux_path_vdpau%': '/usr/local',
|
||||
'linux_path_breakpad%': '/usr/local',
|
||||
'linux_path_opus_include%': '<(libs_loc)/opus/include',
|
||||
'linux_path_range%': '/usr/local',
|
||||
},
|
||||
'include_dirs': [
|
||||
'/usr/local/include',
|
||||
'<(linux_path_ffmpeg)/include',
|
||||
'<(linux_path_openal)/include',
|
||||
'<(linux_path_breakpad)/include/breakpad',
|
||||
'<(linux_path_opus_include)',
|
||||
'<(linux_path_range)/include',
|
||||
],
|
||||
'library_dirs': [
|
||||
'/usr/local/lib',
|
||||
'<(linux_path_ffmpeg)/lib',
|
||||
'<(linux_path_openal)/lib',
|
||||
'<(linux_path_va)/lib',
|
||||
'<(linux_path_vdpau)/lib',
|
||||
'<(linux_path_breakpad)/lib',
|
||||
],
|
||||
'conditions': [
|
||||
[ '"<!(uname -m)" == "x86_64" or "<!(uname -m)" == "aarch64"', {
|
||||
'defines': [
|
||||
'Q_OS_LINUX64',
|
||||
],
|
||||
'conditions': [
|
||||
[ '"<(official_build_target)" != "" and "<(official_build_target)" != "linux"', {
|
||||
'sources': [ '__Wrong_Official_Build_Target_<(official_build_target)_' ],
|
||||
}],
|
||||
],
|
||||
}, {
|
||||
'defines': [
|
||||
'Q_OS_LINUX32',
|
||||
],
|
||||
'conditions': [
|
||||
[ '"<(official_build_target)" != "" and "<(official_build_target)" != "linux32"', {
|
||||
'sources': [ '__Wrong_Official_Build_Target_<(official_build_target)_' ],
|
||||
}],
|
||||
],
|
||||
}], [ '"<!(uname -p)" == "x86_64"', {
|
||||
# 32 bit version can't be linked with debug info or LTO,
|
||||
# virtual memory exhausted :(
|
||||
'cflags_c': [ '-g' ],
|
||||
'cflags_cc': [ '-g' ],
|
||||
'ldflags': [ '-g' ],
|
||||
'configurations': {
|
||||
'Release': {
|
||||
'cflags_c': [ '-flto' ],
|
||||
'cflags_cc': [ '-flto' ],
|
||||
'ldflags': [ '-flto', '-fuse-linker-plugin' ],
|
||||
},
|
||||
},
|
||||
}]
|
||||
],
|
||||
'defines': [
|
||||
'_REENTRANT',
|
||||
'QT_STATICPLUGIN',
|
||||
'QT_PLUGIN',
|
||||
],
|
||||
'cflags_c': [
|
||||
'<@(linux_common_flags)',
|
||||
'-std=gnu11',
|
||||
],
|
||||
'cflags_cc': [
|
||||
'<@(linux_common_flags)',
|
||||
'-std=c++1z',
|
||||
'-Wno-register',
|
||||
],
|
||||
'make_global_settings': [
|
||||
['AR', '/usr/bin/gcc-ar'],
|
||||
['RANLIB', '/usr/bin/gcc-ranlib'],
|
||||
['NM', '/usr/bin/gcc-nm'],
|
||||
],
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
# This file is part of Telegram Desktop,
|
||||
# the official desktop application for the Telegram messaging service.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
{
|
||||
'conditions': [
|
||||
[ 'build_mac', {
|
||||
'variables': {
|
||||
'mac_frameworks': [
|
||||
'Cocoa',
|
||||
'CoreFoundation',
|
||||
'CoreServices',
|
||||
'CoreText',
|
||||
'CoreGraphics',
|
||||
'OpenGL',
|
||||
'AudioUnit',
|
||||
'ApplicationServices',
|
||||
'Foundation',
|
||||
'AGL',
|
||||
'Security',
|
||||
'SystemConfiguration',
|
||||
'Carbon',
|
||||
'AudioToolbox',
|
||||
'CoreAudio',
|
||||
'QuartzCore',
|
||||
'AppKit',
|
||||
'CoreWLAN',
|
||||
'IOKit',
|
||||
],
|
||||
'mac_common_flags': [
|
||||
'-pipe',
|
||||
'-g',
|
||||
'-Wall',
|
||||
'-Werror',
|
||||
'-W',
|
||||
'-fPIE',
|
||||
'-Wno-unused-variable',
|
||||
'-Wno-unused-parameter',
|
||||
'-Wno-unused-function',
|
||||
'-Wno-switch',
|
||||
'-Wno-comment',
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-sign-compare',
|
||||
'-Wno-unknown-attributes',
|
||||
],
|
||||
},
|
||||
'xcode_settings': {
|
||||
'SYMROOT': '../../out',
|
||||
'OTHER_CFLAGS': [
|
||||
'<@(mac_common_flags)',
|
||||
],
|
||||
'OTHER_CPLUSPLUSFLAGS': [
|
||||
'<@(mac_common_flags)',
|
||||
],
|
||||
'OTHER_LDFLAGS': [
|
||||
'<!@(python -c "for s in \'<@(mac_frameworks)\'.split(\' \'): print(\'-framework \' + s)")',
|
||||
],
|
||||
'MACOSX_DEPLOYMENT_TARGET': '<(mac_target)',
|
||||
'COMBINE_HIDPI_IMAGES': 'YES',
|
||||
'COPY_PHASE_STRIP': 'NO',
|
||||
'CLANG_CXX_LANGUAGE_STANDARD': 'c++1z',
|
||||
'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
|
||||
'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES',
|
||||
'GCC_OPTIMIZATION_LEVEL': '0',
|
||||
'GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS': 'NO', # temp for range-v3
|
||||
'ALWAYS_SEARCH_USER_PATHS': 'NO',
|
||||
},
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
'xcode_settings': {
|
||||
'ENABLE_TESTABILITY': 'YES',
|
||||
'ONLY_ACTIVE_ARCH': 'YES',
|
||||
},
|
||||
},
|
||||
},
|
||||
'conditions': [
|
||||
[ '"<(official_build_target)" != "" and "<(official_build_target)" != "mac" and "<(official_build_target)" != "mac32" and "<(official_build_target)" != "macstore"', {
|
||||
'sources': [ '__Wrong_Official_Build_Target__' ],
|
||||
}],
|
||||
],
|
||||
}],
|
||||
[ 'build_macold', {
|
||||
'xcode_settings': {
|
||||
'OTHER_CPLUSPLUSFLAGS': [
|
||||
'-Wno-inconsistent-missing-override',
|
||||
],
|
||||
'OTHER_LDFLAGS': [
|
||||
'-w', # Suppress 'libstdc++ is deprecated' warning.
|
||||
],
|
||||
'GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS': 'NO', # temp for range-v3
|
||||
},
|
||||
'defines': [
|
||||
'OS_MAC_OLD',
|
||||
'RANGES_CXX_THREAD_LOCAL=0',
|
||||
],
|
||||
}, {
|
||||
'xcode_settings': {
|
||||
'CLANG_CXX_LIBRARY': 'libc++',
|
||||
'CLANG_ENABLE_OBJC_WEAK': 'YES',
|
||||
'OTHER_LDFLAGS': [
|
||||
'-framework', 'VideoToolbox',
|
||||
'-framework', 'VideoDecodeAcceleration',
|
||||
'-framework', 'AVFoundation',
|
||||
'-framework', 'CoreMedia',
|
||||
],
|
||||
},
|
||||
}],
|
||||
[ 'build_macstore', {
|
||||
'defines': [
|
||||
'TDESKTOP_DISABLE_AUTOUPDATE',
|
||||
'OS_MAC_STORE',
|
||||
],
|
||||
}]
|
||||
],
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
# This file is part of Telegram Desktop,
|
||||
# the official desktop application for the Telegram messaging service.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
{
|
||||
'conditions': [
|
||||
[ 'build_win', {
|
||||
'defines': [
|
||||
'WIN32',
|
||||
'_WINDOWS',
|
||||
'_UNICODE',
|
||||
'UNICODE',
|
||||
'HAVE_STDINT_H',
|
||||
'ZLIB_WINAPI',
|
||||
'_SCL_SECURE_NO_WARNINGS',
|
||||
'_USING_V110_SDK71_',
|
||||
],
|
||||
'msbuild_toolset': 'v142',
|
||||
'msvs_cygwin_shell': 0,
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
'ProgramDataBaseFileName': '$(OutDir)\\$(ProjectName).pdb',
|
||||
'DebugInformationFormat': '3', # Program Database (/Zi)
|
||||
'WarnAsError': 'true',
|
||||
'AdditionalOptions': [
|
||||
'/std:<(build_standard_win)',
|
||||
'/permissive-',
|
||||
'/Qspectre',
|
||||
'/MP', # Enable multi process build.
|
||||
'/EHsc', # Catch C++ exceptions only, extern C functions never throw a C++ exception.
|
||||
'/w14834', # [[nodiscard]]
|
||||
'/w15038', # wrong initialization order
|
||||
'/w14265', # class has virtual functions, but destructor is not virtual
|
||||
'/experimental:preprocessor', # need for range-v3 see https://github.com/ericniebler/range-v3#supported-compilers
|
||||
'/wd5105', # needed for `/experimental:preprocessor`, suppressing C5105 "macro expansion producing 'defined' has undefined behavior"
|
||||
],
|
||||
'TreatWChar_tAsBuiltInType': 'false',
|
||||
},
|
||||
'VCLinkerTool': {
|
||||
'MinimumRequiredVersion': '5.01',
|
||||
'ImageHasSafeExceptionHandlers': 'false', # Disable /SAFESEH
|
||||
},
|
||||
},
|
||||
'msvs_external_builder_build_cmd': [
|
||||
'ninja.exe',
|
||||
'-C',
|
||||
'$(OutDir)',
|
||||
'-k0',
|
||||
'$(ProjectName)',
|
||||
],
|
||||
'libraries': [
|
||||
'-lwinmm',
|
||||
'-limm32',
|
||||
'-lws2_32',
|
||||
'-lkernel32',
|
||||
'-luser32',
|
||||
'-lgdi32',
|
||||
'-lwinspool',
|
||||
'-lcomdlg32',
|
||||
'-ladvapi32',
|
||||
'-lshell32',
|
||||
'-lole32',
|
||||
'-loleaut32',
|
||||
'-luuid',
|
||||
'-lodbc32',
|
||||
'-lodbccp32',
|
||||
'-lShlwapi',
|
||||
'-lIphlpapi',
|
||||
'-lGdiplus',
|
||||
'-lStrmiids',
|
||||
],
|
||||
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
'Optimization': '0', # Disabled (/Od)
|
||||
'RuntimeLibrary': '1', # Multi-threaded Debug (/MTd)
|
||||
},
|
||||
'VCLinkerTool': {
|
||||
'GenerateDebugInformation': 'true', # true (/DEBUG)
|
||||
'IgnoreDefaultLibraryNames': 'LIBCMT',
|
||||
'LinkIncremental': '2', # Yes (/INCREMENTAL)
|
||||
},
|
||||
},
|
||||
},
|
||||
'Release': {
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
'Optimization': '2', # Maximize Speed (/O2)
|
||||
'InlineFunctionExpansion': '2', # Any suitable (/Ob2)
|
||||
'EnableIntrinsicFunctions': 'true', # Yes (/Oi)
|
||||
'FavorSizeOrSpeed': '1', # Favor fast code (/Ot)
|
||||
'RuntimeLibrary': '0', # Multi-threaded (/MT)
|
||||
'EnableEnhancedInstructionSet': '2', # Streaming SIMD Extensions 2 (/arch:SSE2)
|
||||
'WholeProgramOptimization': 'true', # /GL
|
||||
},
|
||||
'VCLinkerTool': {
|
||||
'GenerateDebugInformation': 'true', # /DEBUG
|
||||
'OptimizeReferences': '2',
|
||||
'LinkTimeCodeGeneration': '1', # /LTCG
|
||||
},
|
||||
'VCLibrarianTool': {
|
||||
'LinkTimeCodeGeneration': 'true', # /LTCG
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'conditions': [
|
||||
[ '"<(official_build_target)" != "" and "<(official_build_target)" != "win" and "<(official_build_target)" != "uwp"', {
|
||||
'sources': [ '__Wrong_Official_Build_Target__' ],
|
||||
}],
|
||||
],
|
||||
}],
|
||||
],
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
# This file is part of Telegram Desktop,
|
||||
# the official desktop application for the Telegram messaging service.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'common/common.gypi',
|
||||
],
|
||||
'targets': [{
|
||||
'target_name': 'lib_base',
|
||||
'includes': [
|
||||
'common/library.gypi',
|
||||
'modules/openssl.gypi',
|
||||
'modules/qt.gypi',
|
||||
'modules/pch.gypi',
|
||||
],
|
||||
'variables': {
|
||||
'src_loc': '../SourceFiles',
|
||||
'res_loc': '../Resources',
|
||||
'official_build_target%': '',
|
||||
'submodules_loc': '../ThirdParty',
|
||||
'pch_source': '<(src_loc)/base/base_pch.cpp',
|
||||
'pch_header': '<(src_loc)/base/base_pch.h',
|
||||
},
|
||||
'defines': [
|
||||
'XXH_INLINE_ALL',
|
||||
],
|
||||
'dependencies': [
|
||||
'../ThirdParty/crl/crl.gyp:crl',
|
||||
],
|
||||
'export_dependent_settings': [
|
||||
'../ThirdParty/crl/crl.gyp:crl',
|
||||
],
|
||||
'include_dirs': [
|
||||
'<(src_loc)',
|
||||
'<(SHARED_INTERMEDIATE_DIR)',
|
||||
'<(libs_loc)/range-v3/include',
|
||||
'<(submodules_loc)/GSL/include',
|
||||
'<(submodules_loc)/variant/include',
|
||||
'<(submodules_loc)/xxHash',
|
||||
],
|
||||
'sources': [
|
||||
'<(src_loc)/base/algorithm.h',
|
||||
'<(src_loc)/base/assertion.h',
|
||||
'<(src_loc)/base/base_integration.h',
|
||||
'<(src_loc)/base/basic_types.h',
|
||||
'<(src_loc)/base/binary_guard.h',
|
||||
'<(src_loc)/base/build_config.h',
|
||||
'<(src_loc)/base/bytes.h',
|
||||
'<(src_loc)/base/crc32hash.cpp',
|
||||
'<(src_loc)/base/crc32hash.h',
|
||||
'<(src_loc)/base/concurrent_timer.cpp',
|
||||
'<(src_loc)/base/concurrent_timer.h',
|
||||
'<(src_loc)/base/flags.h',
|
||||
'<(src_loc)/base/enum_mask.h',
|
||||
'<(src_loc)/base/flat_map.h',
|
||||
'<(src_loc)/base/flat_set.h',
|
||||
'<(src_loc)/base/functors.h',
|
||||
'<(src_loc)/base/index_based_iterator.h',
|
||||
'<(src_loc)/base/invoke_queued.h',
|
||||
'<(src_loc)/base/last_used_cache.h',
|
||||
'<(src_loc)/base/match_method.h',
|
||||
'<(src_loc)/base/object_ptr.h',
|
||||
'<(src_loc)/base/observer.cpp',
|
||||
'<(src_loc)/base/observer.h',
|
||||
'<(src_loc)/base/ordered_set.h',
|
||||
'<(src_loc)/base/openssl_help.h',
|
||||
'<(src_loc)/base/optional.h',
|
||||
'<(src_loc)/base/overload.h',
|
||||
'<(src_loc)/base/parse_helper.cpp',
|
||||
'<(src_loc)/base/parse_helper.h',
|
||||
'<(src_loc)/base/qthelp_regex.h',
|
||||
'<(src_loc)/base/qthelp_url.cpp',
|
||||
'<(src_loc)/base/qthelp_url.h',
|
||||
'<(src_loc)/base/qt_connection.h',
|
||||
'<(src_loc)/base/qt_signal_producer.h',
|
||||
'<(src_loc)/base/runtime_composer.cpp',
|
||||
'<(src_loc)/base/runtime_composer.h',
|
||||
'<(src_loc)/base/thread_safe_wrap.h',
|
||||
'<(src_loc)/base/timer.cpp',
|
||||
'<(src_loc)/base/timer.h',
|
||||
'<(src_loc)/base/type_traits.h',
|
||||
'<(src_loc)/base/unique_any.h',
|
||||
'<(src_loc)/base/unique_function.h',
|
||||
'<(src_loc)/base/unique_qptr.h',
|
||||
'<(src_loc)/base/unixtime.cpp',
|
||||
'<(src_loc)/base/unixtime.h',
|
||||
'<(src_loc)/base/value_ordering.h',
|
||||
'<(src_loc)/base/variant.h',
|
||||
'<(src_loc)/base/virtual_method.h',
|
||||
'<(src_loc)/base/weak_ptr.h',
|
||||
'<(src_loc)/base/zlib_help.h',
|
||||
],
|
||||
'conditions': [[ 'build_macold', {
|
||||
'xcode_settings': {
|
||||
'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ],
|
||||
},
|
||||
'include_dirs': [
|
||||
'/usr/local/macold/include/c++/v1',
|
||||
],
|
||||
}]],
|
||||
}],
|
||||
}
|
|
@ -6,21 +6,20 @@
|
|||
|
||||
{
|
||||
'includes': [
|
||||
'common/common.gypi',
|
||||
'../ThirdParty/gyp_helpers/common/common.gypi',
|
||||
],
|
||||
'targets': [{
|
||||
'target_name': 'lib_export',
|
||||
'type': 'static_library',
|
||||
'includes': [
|
||||
'common/library.gypi',
|
||||
'modules/qt.gypi',
|
||||
'modules/pch.gypi',
|
||||
'../ThirdParty/gyp_helpers/common/library.gypi',
|
||||
'../ThirdParty/gyp_helpers/modules/qt.gypi',
|
||||
'../ThirdParty/gyp_helpers/modules/pch.gypi',
|
||||
],
|
||||
'variables': {
|
||||
'src_loc': '../SourceFiles',
|
||||
'res_loc': '../Resources',
|
||||
'official_build_target%': '',
|
||||
'submodules_loc': '../ThirdParty',
|
||||
'pch_source': '<(src_loc)/export/export_pch.cpp',
|
||||
'pch_header': '<(src_loc)/export/export_pch.h',
|
||||
},
|
||||
|
@ -28,11 +27,11 @@
|
|||
],
|
||||
'dependencies': [
|
||||
'lib_scheme.gyp:lib_scheme',
|
||||
'lib_base.gyp:lib_base',
|
||||
'../ThirdParty/lib_base/lib_base.gyp:lib_base',
|
||||
],
|
||||
'export_dependent_settings': [
|
||||
'lib_scheme.gyp:lib_scheme',
|
||||
'lib_base.gyp:lib_base',
|
||||
'../ThirdParty/lib_base/lib_base.gyp:lib_base',
|
||||
],
|
||||
'conditions': [[ 'build_macold', {
|
||||
'xcode_settings': {
|
||||
|
@ -44,10 +43,6 @@
|
|||
}]],
|
||||
'include_dirs': [
|
||||
'<(src_loc)',
|
||||
'<(SHARED_INTERMEDIATE_DIR)',
|
||||
'<(libs_loc)/range-v3/include',
|
||||
'<(submodules_loc)/GSL/include',
|
||||
'<(submodules_loc)/variant/include',
|
||||
],
|
||||
'sources': [
|
||||
'<(src_loc)/export/export_api_wrap.cpp',
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue