Rocket.Chat.ReactNative/ios/Pods/Flipper-RSocket/yarpl/observable/ObservableOperator.h

561 lines
16 KiB
C++

// Copyright (c) Facebook, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <utility>
#include <folly/functional/Invoke.h>
#include "yarpl/Observable.h"
#include "yarpl/observable/Observer.h"
#include "yarpl/observable/Observable.h"
namespace yarpl {
namespace observable {
/**
* Base (helper) class for operators. Operators are templated on two types:
* D (downstream) and U (upstream). Operators are created by method calls on
* an upstream Observable, and are Observables themselves. Multi-stage
* pipelines
* can be built: a Observable heading a sequence of Operators.
*/
template <typename U, typename D>
class ObservableOperator : public Observable<D> {
protected:
/// An Operator's subscription.
///
/// When a pipeline chain is active, each Observable has a corresponding
/// subscription. Except for the first one, the subscriptions are created
/// against Operators. Each operator subscription has two functions: as a
/// subscriber for the previous stage; as a subscription for the next one,
/// the user-supplied subscriber being the last of the pipeline stages.
class OperatorSubscription : public ::yarpl::observable::Subscription,
public Observer<U> {
protected:
explicit OperatorSubscription(std::shared_ptr<Observer<D>> observer)
: observer_(std::move(observer)) {
assert(observer_);
}
void observerOnNext(D value) {
if (observer_) {
observer_->onNext(std::move(value));
}
}
/// Terminates both ends of an operator normally.
void terminate() {
terminateImpl(TerminateState::Both());
}
/// Terminates both ends of an operator with an error.
void terminateErr(folly::exception_wrapper ex) {
terminateImpl(TerminateState::Both(), std::move(ex));
}
// Subscription.
void cancel() override {
Subscription::cancel();
terminateImpl(TerminateState::Up());
}
// Observer.
void onSubscribe(std::shared_ptr<yarpl::observable::Subscription>
subscription) override {
if (upstream_) {
DLOG(ERROR) << "attempt to subscribe twice";
subscription->cancel();
return;
}
upstream_ = std::move(subscription);
observer_->onSubscribe(this->ref_from_this(this));
}
void onComplete() override {
terminateImpl(TerminateState::Down());
}
void onError(folly::exception_wrapper ex) override {
terminateImpl(TerminateState::Down(), std::move(ex));
}
private:
struct TerminateState {
TerminateState(bool u, bool d) : up{u}, down{d} {}
static TerminateState Down() {
return TerminateState{false, true};
}
static TerminateState Up() {
return TerminateState{true, false};
}
static TerminateState Both() {
return TerminateState{true, true};
}
const bool up{false};
const bool down{false};
};
bool isTerminated() const {
return !upstream_ && !observer_;
}
/// Terminates an operator, sending cancel() and on{Complete,Error}()
/// signals as necessary.
void terminateImpl(
TerminateState state,
folly::exception_wrapper ex = folly::exception_wrapper{nullptr}) {
if (isTerminated()) {
return;
}
if (auto upstream = std::move(upstream_)) {
if (state.up) {
upstream->cancel();
}
}
if (auto observer = std::move(observer_)) {
if (state.down) {
if (ex) {
observer->onError(std::move(ex));
} else {
observer->onComplete();
}
}
}
}
/// This subscription controls the life-cycle of the observer. The
/// observer is retained as long as calls on it can be made. (Note:
/// the observer in turn maintains a reference on this subscription
/// object until cancellation and/or completion.)
std::shared_ptr<Observer<D>> observer_;
/// In an active pipeline, cancel and (possibly modified) request(n)
/// calls should be forwarded upstream. Note that `this` is also a
/// observer for the upstream stage: thus, there are cycles; all of
/// the objects drop their references at cancel/complete.
// TODO(lehecka): this is extra field... base class has this member so
// remove it
std::shared_ptr<::yarpl::observable::Subscription> upstream_;
};
};
template <typename U, typename D, typename F>
class MapOperator : public ObservableOperator<U, D> {
using Super = ObservableOperator<U, D>;
static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed");
static_assert(folly::is_invocable_r<D, F, U>::value, "not invocable");
public:
template <typename Func>
MapOperator(std::shared_ptr<Observable<U>> upstream, Func&& function)
: upstream_(std::move(upstream)),
function_(std::forward<Func>(function)) {}
std::shared_ptr<Subscription> subscribe(
std::shared_ptr<Observer<D>> observer) override {
auto subscription = std::make_shared<MapSubscription>(
this->ref_from_this(this), std::move(observer));
upstream_->subscribe(
// Note: implicit cast to a reference to a observer.
subscription);
return subscription;
}
private:
class MapSubscription : public Super::OperatorSubscription {
using SuperSub = typename Super::OperatorSubscription;
public:
MapSubscription(
std::shared_ptr<MapOperator> observable,
std::shared_ptr<Observer<D>> observer)
: SuperSub(std::move(observer)), observable_(std::move(observable)) {}
void onNext(U value) override {
try {
this->observerOnNext(observable_->function_(std::move(value)));
} catch (const std::exception& exn) {
folly::exception_wrapper ew{std::current_exception(), exn};
this->terminateErr(std::move(ew));
}
}
private:
std::shared_ptr<MapOperator> observable_;
};
std::shared_ptr<Observable<U>> upstream_;
F function_;
};
template <typename U, typename F>
class FilterOperator : public ObservableOperator<U, U> {
using Super = ObservableOperator<U, U>;
static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed");
static_assert(folly::is_invocable_r<bool, F, U>::value, "not invocable");
public:
template <typename Func>
FilterOperator(std::shared_ptr<Observable<U>> upstream, Func&& function)
: upstream_(std::move(upstream)),
function_(std::forward<Func>(function)) {}
std::shared_ptr<Subscription> subscribe(
std::shared_ptr<Observer<U>> observer) override {
auto subscription = std::make_shared<FilterSubscription>(
this->ref_from_this(this), std::move(observer));
upstream_->subscribe(
// Note: implicit cast to a reference to a observer.
subscription);
return subscription;
}
private:
class FilterSubscription : public Super::OperatorSubscription {
using SuperSub = typename Super::OperatorSubscription;
public:
FilterSubscription(
std::shared_ptr<FilterOperator> observable,
std::shared_ptr<Observer<U>> observer)
: SuperSub(std::move(observer)), observable_(std::move(observable)) {}
void onNext(U value) override {
if (observable_->function_(value)) {
SuperSub::observerOnNext(std::move(value));
}
}
private:
std::shared_ptr<FilterOperator> observable_;
};
std::shared_ptr<Observable<U>> upstream_;
F function_;
};
template <typename U, typename D, typename F>
class ReduceOperator : public ObservableOperator<U, D> {
using Super = ObservableOperator<U, D>;
static_assert(std::is_same<std::decay_t<F>, F>::value, "undecayed");
static_assert(std::is_assignable<D&, U>::value, "not assignable");
static_assert(folly::is_invocable_r<D, F, D, U>::value, "not invocable");
public:
template <typename Func>
ReduceOperator(std::shared_ptr<Observable<U>> upstream, Func&& function)
: upstream_(std::move(upstream)),
function_(std::forward<Func>(function)) {}
std::shared_ptr<Subscription> subscribe(
std::shared_ptr<Observer<D>> subscriber) override {
auto subscription = std::make_shared<ReduceSubscription>(
this->ref_from_this(this), std::move(subscriber));
upstream_->subscribe(
// Note: implicit cast to a reference to a subscriber.
subscription);
return subscription;
}
private:
class ReduceSubscription : public Super::OperatorSubscription {
using SuperSub = typename Super::OperatorSubscription;
public:
ReduceSubscription(
std::shared_ptr<ReduceOperator> observable,
std::shared_ptr<Observer<D>> observer)
: SuperSub(std::move(observer)),
observable_(std::move(observable)),
accInitialized_(false) {}
void onNext(U value) override {
if (accInitialized_) {
acc_ = observable_->function_(std::move(acc_), std::move(value));
} else {
acc_ = std::move(value);
accInitialized_ = true;
}
}
void onComplete() override {
if (accInitialized_) {
SuperSub::observerOnNext(std::move(acc_));
}
SuperSub::onComplete();
}
private:
std::shared_ptr<ReduceOperator> observable_;
bool accInitialized_;
D acc_;
};
std::shared_ptr<Observable<U>> upstream_;
F function_;
};
template <typename T>
class TakeOperator : public ObservableOperator<T, T> {
using Super = ObservableOperator<T, T>;
public:
TakeOperator(std::shared_ptr<Observable<T>> upstream, int64_t limit)
: upstream_(std::move(upstream)), limit_(limit) {}
std::shared_ptr<Subscription> subscribe(
std::shared_ptr<Observer<T>> observer) override {
auto subscription =
std::make_shared<TakeSubscription>(limit_, std::move(observer));
upstream_->subscribe(subscription);
return subscription;
}
private:
class TakeSubscription : public Super::OperatorSubscription {
using SuperSub = typename Super::OperatorSubscription;
public:
TakeSubscription(int64_t limit, std::shared_ptr<Observer<T>> observer)
: SuperSub(std::move(observer)), limit_(limit) {}
void onSubscribe(std::shared_ptr<yarpl::observable::Subscription>
subscription) override {
SuperSub::onSubscribe(std::move(subscription));
if (limit_ <= 0) {
SuperSub::terminate();
}
}
void onNext(T value) override {
if (limit_-- > 0) {
SuperSub::observerOnNext(std::move(value));
if (limit_ == 0) {
SuperSub::terminate();
}
}
}
private:
int64_t limit_;
};
std::shared_ptr<Observable<T>> upstream_;
const int64_t limit_;
};
template <typename T>
class SkipOperator : public ObservableOperator<T, T> {
using Super = ObservableOperator<T, T>;
public:
SkipOperator(std::shared_ptr<Observable<T>> upstream, int64_t offset)
: upstream_(std::move(upstream)), offset_(offset) {}
std::shared_ptr<Subscription> subscribe(
std::shared_ptr<Observer<T>> observer) override {
auto subscription =
std::make_shared<SkipSubscription>(offset_, std::move(observer));
upstream_->subscribe(subscription);
return subscription;
}
private:
class SkipSubscription : public Super::OperatorSubscription {
using SuperSub = typename Super::OperatorSubscription;
public:
SkipSubscription(int64_t offset, std::shared_ptr<Observer<T>> observer)
: SuperSub(std::move(observer)), offset_(offset) {}
void onNext(T value) override {
if (offset_ <= 0) {
SuperSub::observerOnNext(std::move(value));
} else {
--offset_;
}
}
private:
int64_t offset_;
};
std::shared_ptr<Observable<T>> upstream_;
const int64_t offset_;
};
template <typename T>
class IgnoreElementsOperator : public ObservableOperator<T, T> {
using Super = ObservableOperator<T, T>;
public:
explicit IgnoreElementsOperator(std::shared_ptr<Observable<T>> upstream)
: upstream_(std::move(upstream)) {}
std::shared_ptr<Subscription> subscribe(
std::shared_ptr<Observer<T>> observer) override {
auto subscription =
std::make_shared<IgnoreElementsSubscription>(std::move(observer));
upstream_->subscribe(subscription);
return subscription;
}
private:
class IgnoreElementsSubscription : public Super::OperatorSubscription {
using SuperSub = typename Super::OperatorSubscription;
public:
IgnoreElementsSubscription(std::shared_ptr<Observer<T>> observer)
: SuperSub(std::move(observer)) {}
void onNext(T) override {}
};
std::shared_ptr<Observable<T>> upstream_;
};
template <typename T>
class SubscribeOnOperator : public ObservableOperator<T, T> {
using Super = ObservableOperator<T, T>;
public:
SubscribeOnOperator(
std::shared_ptr<Observable<T>> upstream,
folly::Executor& executor)
: upstream_(std::move(upstream)), executor_(executor) {}
std::shared_ptr<Subscription> subscribe(
std::shared_ptr<Observer<T>> observer) override {
auto subscription = std::make_shared<SubscribeOnSubscription>(
executor_, std::move(observer));
executor_.add([subscription, upstream = upstream_]() mutable {
upstream->subscribe(std::move(subscription));
});
return subscription;
}
private:
class SubscribeOnSubscription : public Super::OperatorSubscription {
using SuperSub = typename Super::OperatorSubscription;
public:
SubscribeOnSubscription(
folly::Executor& executor,
std::shared_ptr<Observer<T>> observer)
: SuperSub(std::move(observer)), executor_(executor) {}
void cancel() override {
executor_.add([self = this->ref_from_this(this), this] {
this->callSuperCancel();
});
}
void onNext(T value) override {
SuperSub::observerOnNext(std::move(value));
}
private:
// Trampoline to call superclass method; gcc bug 58972.
void callSuperCancel() {
SuperSub::cancel();
}
folly::Executor& executor_;
};
std::shared_ptr<Observable<T>> upstream_;
folly::Executor& executor_;
};
template <typename T, typename OnSubscribe>
class FromPublisherOperator : public Observable<T> {
static_assert(
std::is_same<std::decay_t<OnSubscribe>, OnSubscribe>::value,
"undecayed");
public:
template <typename F>
explicit FromPublisherOperator(F&& function)
: function_(std::forward<F>(function)) {}
private:
class PublisherObserver : public Observer<T> {
public:
PublisherObserver(
std::shared_ptr<Observer<T>> inner,
std::shared_ptr<Subscription> subscription)
: inner_(std::move(inner)) {
Observer<T>::onSubscribe(std::move(subscription));
}
void onSubscribe(std::shared_ptr<Subscription>) override {
DLOG(ERROR) << "not allowed to call";
CHECK(false);
}
void onComplete() override {
if (auto inner = atomic_exchange(&inner_, nullptr)) {
inner->onComplete();
}
Observer<T>::onComplete();
}
void onError(folly::exception_wrapper ex) override {
if (auto inner = atomic_exchange(&inner_, nullptr)) {
inner->onError(std::move(ex));
}
Observer<T>::onError(folly::exception_wrapper());
}
void onNext(T t) override {
atomic_load(&inner_)->onNext(std::move(t));
}
private:
AtomicReference<Observer<T>> inner_;
};
public:
std::shared_ptr<Subscription> subscribe(
std::shared_ptr<Observer<T>> observer) override {
auto subscription = Subscription::create();
observer->onSubscribe(subscription);
if (!subscription->isCancelled()) {
function_(std::make_shared<PublisherObserver>(
std::move(observer), subscription), subscription);
}
return subscription;
}
private:
OnSubscribe function_;
};
} // namespace observable
} // namespace yarpl
#include "yarpl/observable/ObservableConcatOperators.h"
#include "yarpl/observable/ObservableDoOperator.h"