Use lib_rpl / lib_base from submodules.

This commit is contained in:
John Preston 2019-09-17 16:11:23 +03:00
parent be9398b05a
commit 1b89348d89
118 changed files with 128 additions and 16366 deletions

View file

@ -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);
}

View file

@ -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);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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.");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}

View file

@ -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);
}
}

View file

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

View file

@ -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);
}
}
}

View file

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

View file

@ -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();
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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(); }
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 &params,
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 &param : 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

View file

@ -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 &params,
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

View file

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

View file

@ -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()]));
}
};

View file

@ -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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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));
}
}

View file

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

View file

@ -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 &current() 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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> &copyCounter,
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

View file

@ -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);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}
}

View file

@ -6,7 +6,7 @@
{
'includes': [
'common/common.gypi',
'../ThirdParty/gyp_helpers/common/common.gypi',
'telegram/telegram.gypi',
],
}

View file

@ -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': [

View file

@ -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',
],
},
},
}

View file

@ -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',
},
},
}

View file

@ -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',
],
}

View file

@ -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': {
},
},
}],
],
}

View file

@ -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',
],
}]
],
}

View file

@ -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__' ],
}],
],
}],
],
}

View file

@ -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',
],
}]],
}],
}

View file

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