Kit.Core/LibExternal/System.Reactive/Linq/Observable/Window.cs

767 lines
25 KiB
C#

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
namespace System.Reactive.Linq.ObservableImpl
{
internal static class Window<TSource>
{
internal sealed class Count : Producer<IObservable<TSource>, Count._>
{
private readonly IObservable<TSource> _source;
private readonly int _count;
private readonly int _skip;
public Count(IObservable<TSource> source, int count, int skip)
{
_source = source;
_count = count;
_skip = skip;
}
protected override _ CreateSink(IObserver<IObservable<TSource>> observer) => new(this, observer);
protected override void Run(_ sink) => sink.Run(_source);
internal sealed class _ : Sink<TSource, IObservable<TSource>>
{
private readonly Queue<ISubject<TSource>> _queue = new();
private readonly SingleAssignmentDisposable _m = new();
private readonly RefCountDisposable _refCountDisposable;
private readonly int _count;
private readonly int _skip;
public _(Count parent, IObserver<IObservable<TSource>> observer)
: base(observer)
{
_refCountDisposable = new RefCountDisposable(_m);
_count = parent._count;
_skip = parent._skip;
}
private int _n;
public override void Run(IObservable<TSource> source)
{
var firstWindow = CreateWindow();
ForwardOnNext(firstWindow);
_m.Disposable = source.SubscribeSafe(this);
SetUpstream(_refCountDisposable);
}
private IObservable<TSource> CreateWindow()
{
var s = new Subject<TSource>();
_queue.Enqueue(s);
return new WindowObservable<TSource>(s, _refCountDisposable);
}
public override void OnNext(TSource value)
{
foreach (var s in _queue)
{
s.OnNext(value);
}
var c = _n - _count + 1;
if (c >= 0 && c % _skip == 0)
{
var s = _queue.Dequeue();
s.OnCompleted();
}
_n++;
if (_n % _skip == 0)
{
var newWindow = CreateWindow();
ForwardOnNext(newWindow);
}
}
public override void OnError(Exception error)
{
while (_queue.Count > 0)
{
_queue.Dequeue().OnError(error);
}
ForwardOnError(error);
}
public override void OnCompleted()
{
while (_queue.Count > 0)
{
_queue.Dequeue().OnCompleted();
}
ForwardOnCompleted();
}
}
}
internal sealed class TimeSliding : Producer<IObservable<TSource>, TimeSliding._>
{
private readonly IObservable<TSource> _source;
private readonly TimeSpan _timeSpan;
private readonly TimeSpan _timeShift;
private readonly IScheduler _scheduler;
public TimeSliding(IObservable<TSource> source, TimeSpan timeSpan, TimeSpan timeShift, IScheduler scheduler)
{
_source = source;
_timeSpan = timeSpan;
_timeShift = timeShift;
_scheduler = scheduler;
}
protected override _ CreateSink(IObserver<IObservable<TSource>> observer) => new(this, observer);
protected override void Run(_ sink) => sink.Run(this);
internal sealed class _ : Sink<TSource, IObservable<TSource>>
{
private readonly object _gate = new();
private readonly Queue<ISubject<TSource>> _q = new();
private readonly SerialDisposable _timerD = new();
private readonly IScheduler _scheduler;
private readonly TimeSpan _timeShift;
public _(TimeSliding parent, IObserver<IObservable<TSource>> observer)
: base(observer)
{
_scheduler = parent._scheduler;
_timeShift = parent._timeShift;
}
private RefCountDisposable? _refCountDisposable;
private TimeSpan _totalTime;
private TimeSpan _nextShift;
private TimeSpan _nextSpan;
public void Run(TimeSliding parent)
{
_totalTime = TimeSpan.Zero;
_nextShift = parent._timeShift;
_nextSpan = parent._timeSpan;
var groupDisposable = new CompositeDisposable(2) { _timerD };
_refCountDisposable = new RefCountDisposable(groupDisposable);
CreateWindow();
CreateTimer();
groupDisposable.Add(parent._source.SubscribeSafe(this));
SetUpstream(_refCountDisposable);
}
private void CreateWindow()
{
var s = new Subject<TSource>();
_q.Enqueue(s);
ForwardOnNext(new WindowObservable<TSource>(s, _refCountDisposable!)); // NB: _refCountDisposable gets assigned in Run.
}
private void CreateTimer()
{
var m = new SingleAssignmentDisposable();
_timerD.Disposable = m;
var isSpan = false;
var isShift = false;
if (_nextSpan == _nextShift)
{
isSpan = true;
isShift = true;
}
else if (_nextSpan < _nextShift)
{
isSpan = true;
}
else
{
isShift = true;
}
var newTotalTime = isSpan ? _nextSpan : _nextShift;
var ts = newTotalTime - _totalTime;
_totalTime = newTotalTime;
if (isSpan)
{
_nextSpan += _timeShift;
}
if (isShift)
{
_nextShift += _timeShift;
}
m.Disposable = _scheduler.ScheduleAction((@this: this, isSpan, isShift), ts, static tuple => tuple.@this.Tick(tuple.isSpan, tuple.isShift));
}
private void Tick(bool isSpan, bool isShift)
{
lock (_gate)
{
//
// BREAKING CHANGE v2 > v1.x - Making behavior of sending OnCompleted to the window
// before sending out a new window consistent across all
// overloads of Window and Buffer. Before v2, the two
// operations below were reversed.
//
if (isSpan)
{
var s = _q.Dequeue();
s.OnCompleted();
}
if (isShift)
{
CreateWindow();
}
}
CreateTimer();
}
public override void OnNext(TSource value)
{
lock (_gate)
{
foreach (var s in _q)
{
s.OnNext(value);
}
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
foreach (var s in _q)
{
s.OnError(error);
}
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
foreach (var s in _q)
{
s.OnCompleted();
}
ForwardOnCompleted();
}
}
}
}
internal sealed class TimeHopping : Producer<IObservable<TSource>, TimeHopping._>
{
private readonly IObservable<TSource> _source;
private readonly TimeSpan _timeSpan;
private readonly IScheduler _scheduler;
public TimeHopping(IObservable<TSource> source, TimeSpan timeSpan, IScheduler scheduler)
{
_source = source;
_timeSpan = timeSpan;
_scheduler = scheduler;
}
protected override _ CreateSink(IObserver<IObservable<TSource>> observer) => new(observer);
protected override void Run(_ sink) => sink.Run(this);
internal sealed class _ : Sink<TSource, IObservable<TSource>>
{
private readonly object _gate = new();
private Subject<TSource> _subject;
public _(IObserver<IObservable<TSource>> observer)
: base(observer)
{
_subject = new Subject<TSource>();
}
private RefCountDisposable? _refCountDisposable;
public void Run(TimeHopping parent)
{
var groupDisposable = new CompositeDisposable(2);
_refCountDisposable = new RefCountDisposable(groupDisposable);
NextWindow();
groupDisposable.Add(parent._scheduler.SchedulePeriodic(this, parent._timeSpan, static @this => @this.Tick()));
groupDisposable.Add(parent._source.SubscribeSafe(this));
SetUpstream(_refCountDisposable);
}
private void Tick()
{
lock (_gate)
{
_subject.OnCompleted();
_subject = new Subject<TSource>();
NextWindow();
}
}
private void NextWindow()
{
ForwardOnNext(new WindowObservable<TSource>(_subject, _refCountDisposable!)); // NB: _refCountDisposable gets assigned in Run.
}
public override void OnNext(TSource value)
{
lock (_gate)
{
_subject.OnNext(value);
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
_subject.OnError(error);
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
_subject.OnCompleted();
ForwardOnCompleted();
}
}
}
}
internal sealed class Ferry : Producer<IObservable<TSource>, Ferry._>
{
private readonly IObservable<TSource> _source;
private readonly int _count;
private readonly TimeSpan _timeSpan;
private readonly IScheduler _scheduler;
public Ferry(IObservable<TSource> source, TimeSpan timeSpan, int count, IScheduler scheduler)
{
_source = source;
_timeSpan = timeSpan;
_count = count;
_scheduler = scheduler;
}
protected override _ CreateSink(IObserver<IObservable<TSource>> observer) => new(this, observer);
protected override void Run(_ sink) => sink.Run(_source);
internal sealed class _ : Sink<TSource, IObservable<TSource>>
{
private readonly object _gate = new();
private readonly SerialDisposable _timerD = new();
private readonly int _count;
private readonly TimeSpan _timeSpan;
private readonly IScheduler _scheduler;
private Subject<TSource> _s;
public _(Ferry parent, IObserver<IObservable<TSource>> observer)
: base(observer)
{
_count = parent._count;
_timeSpan = parent._timeSpan;
_scheduler = parent._scheduler;
_s = new Subject<TSource>();
}
private int _n;
private RefCountDisposable? _refCountDisposable;
public override void Run(IObservable<TSource> source)
{
var groupDisposable = new CompositeDisposable(2) { _timerD };
_refCountDisposable = new RefCountDisposable(groupDisposable);
NextWindow();
CreateTimer(_s);
groupDisposable.Add(source.SubscribeSafe(this));
SetUpstream(_refCountDisposable);
}
private void CreateTimer(Subject<TSource> window)
{
var m = new SingleAssignmentDisposable();
_timerD.Disposable = m;
m.Disposable = _scheduler.ScheduleAction((@this: this, window), _timeSpan, static tuple => tuple.@this.Tick(tuple.window));
}
private void NextWindow()
{
ForwardOnNext(new WindowObservable<TSource>(_s, _refCountDisposable!)); // NB: _refCountDisposable gets assigned in Run.
}
private void Tick(Subject<TSource> window)
{
Subject<TSource> newWindow;
lock (_gate)
{
if (window != _s)
{
return;
}
_n = 0;
newWindow = new Subject<TSource>();
_s.OnCompleted();
_s = newWindow;
NextWindow();
}
CreateTimer(newWindow);
}
public override void OnNext(TSource value)
{
Subject<TSource>? newWindow = null;
lock (_gate)
{
_s.OnNext(value);
_n++;
if (_n == _count)
{
_n = 0;
newWindow = new Subject<TSource>();
_s.OnCompleted();
_s = newWindow;
NextWindow();
}
}
if (newWindow != null)
{
CreateTimer(newWindow);
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
_s.OnError(error);
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
_s.OnCompleted();
ForwardOnCompleted();
}
}
}
}
}
internal static class Window<TSource, TWindowClosing>
{
internal sealed class Selector : Producer<IObservable<TSource>, Selector._>
{
private readonly IObservable<TSource> _source;
private readonly Func<IObservable<TWindowClosing>> _windowClosingSelector;
public Selector(IObservable<TSource> source, Func<IObservable<TWindowClosing>> windowClosingSelector)
{
_source = source;
_windowClosingSelector = windowClosingSelector;
}
protected override _ CreateSink(IObserver<IObservable<TSource>> observer) => new(this, observer);
protected override void Run(_ sink) => sink.Run(_source);
internal sealed class _ : Sink<TSource, IObservable<TSource>>
{
private readonly object _gate = new();
private readonly AsyncLock _windowGate = new();
private readonly SerialDisposable _m = new();
private readonly Func<IObservable<TWindowClosing>> _windowClosingSelector;
private Subject<TSource> _window;
public _(Selector parent, IObserver<IObservable<TSource>> observer)
: base(observer)
{
_windowClosingSelector = parent._windowClosingSelector;
_window = new Subject<TSource>();
}
private RefCountDisposable? _refCountDisposable;
public override void Run(IObservable<TSource> source)
{
var groupDisposable = new CompositeDisposable(2) { _m };
_refCountDisposable = new RefCountDisposable(groupDisposable);
NextWindow();
groupDisposable.Add(source.SubscribeSafe(this));
_windowGate.Wait(this, static @this => @this.CreateWindowClose());
SetUpstream(_refCountDisposable);
}
private void NextWindow()
{
var window = new WindowObservable<TSource>(_window, _refCountDisposable!); // NB: _refCountDisposable gets assigned in Run.
ForwardOnNext(window);
}
private void CreateWindowClose()
{
IObservable<TWindowClosing> windowClose;
try
{
windowClose = _windowClosingSelector();
}
catch (Exception exception)
{
lock (_gate)
{
ForwardOnError(exception);
}
return;
}
var observer = new WindowClosingObserver(this);
_m.Disposable = observer;
observer.SetResource(windowClose.SubscribeSafe(observer));
}
private void CloseWindow(IDisposable closingSubscription)
{
closingSubscription.Dispose();
lock (_gate)
{
_window.OnCompleted();
_window = new Subject<TSource>();
NextWindow();
}
_windowGate.Wait(this, static @this => @this.CreateWindowClose());
}
private sealed class WindowClosingObserver : SafeObserver<TWindowClosing>
{
private readonly _ _parent;
public WindowClosingObserver(_ parent)
{
_parent = parent;
}
public override void OnNext(TWindowClosing value)
{
_parent.CloseWindow(this);
}
public override void OnError(Exception error)
{
_parent.OnError(error);
}
public override void OnCompleted()
{
_parent.CloseWindow(this);
}
}
public override void OnNext(TSource value)
{
lock (_gate)
{
_window.OnNext(value);
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
_window.OnError(error);
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
_window.OnCompleted();
ForwardOnCompleted();
}
}
}
}
internal sealed class Boundaries : Producer<IObservable<TSource>, Boundaries._>
{
private readonly IObservable<TSource> _source;
private readonly IObservable<TWindowClosing> _windowBoundaries;
public Boundaries(IObservable<TSource> source, IObservable<TWindowClosing> windowBoundaries)
{
_source = source;
_windowBoundaries = windowBoundaries;
}
protected override _ CreateSink(IObserver<IObservable<TSource>> observer) => new(observer);
protected override void Run(_ sink) => sink.Run(this);
internal sealed class _ : Sink<TSource, IObservable<TSource>>
{
private readonly object _gate = new();
private Subject<TSource> _window;
public _(IObserver<IObservable<TSource>> observer)
: base(observer)
{
_window = new Subject<TSource>();
}
private RefCountDisposable? _refCountDisposable;
public void Run(Boundaries parent)
{
var d = new CompositeDisposable(2);
_refCountDisposable = new RefCountDisposable(d);
NextWindow();
d.Add(parent._source.SubscribeSafe(this));
d.Add(parent._windowBoundaries.SubscribeSafe(new WindowClosingObserver(this)));
SetUpstream(_refCountDisposable);
}
private void NextWindow()
{
var window = new WindowObservable<TSource>(_window, _refCountDisposable!); // NB: _refCountDisposable gets assigned in Run.
ForwardOnNext(window);
}
private sealed class WindowClosingObserver : IObserver<TWindowClosing>
{
private readonly _ _parent;
public WindowClosingObserver(_ parent)
{
_parent = parent;
}
public void OnNext(TWindowClosing value)
{
lock (_parent._gate)
{
_parent._window.OnCompleted();
_parent._window = new Subject<TSource>();
_parent.NextWindow();
}
}
public void OnError(Exception error)
{
_parent.OnError(error);
}
public void OnCompleted()
{
_parent.OnCompleted();
}
}
public override void OnNext(TSource value)
{
lock (_gate)
{
_window.OnNext(value);
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
_window.OnError(error);
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
_window.OnCompleted();
ForwardOnCompleted();
}
}
}
}
}
internal sealed class WindowObservable<TSource> : AddRef<TSource>
{
public WindowObservable(IObservable<TSource> source, RefCountDisposable refCount)
: base(source, refCount)
{
}
}
}