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

871 lines
28 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;
namespace System.Reactive.Linq.ObservableImpl
{
internal static class Buffer<TSource>
{
internal sealed class CountExact : Producer<IList<TSource>, CountExact.ExactSink>
{
private readonly IObservable<TSource> _source;
private readonly int _count;
public CountExact(IObservable<TSource> source, int count)
{
_source = source;
_count = count;
}
protected override ExactSink CreateSink(IObserver<IList<TSource>> observer) => new(observer, _count);
protected override void Run(ExactSink sink) => sink.Run(_source);
internal sealed class ExactSink : Sink<TSource, IList<TSource>>
{
private readonly int _count;
private int _index;
private IList<TSource>? _buffer;
internal ExactSink(IObserver<IList<TSource>> observer, int count) : base(observer)
{
_count = count;
}
public override void OnNext(TSource value)
{
var buffer = _buffer;
if (buffer == null)
{
buffer = new List<TSource>();
_buffer = buffer;
}
buffer.Add(value);
var idx = _index + 1;
if (idx == _count)
{
_buffer = null;
_index = 0;
ForwardOnNext(buffer);
}
else
{
_index = idx;
}
}
public override void OnError(Exception error)
{
_buffer = null;
ForwardOnError(error);
}
public override void OnCompleted()
{
var buffer = _buffer;
_buffer = null;
if (buffer != null)
{
ForwardOnNext(buffer);
}
ForwardOnCompleted();
}
}
}
internal sealed class CountSkip : Producer<IList<TSource>, CountSkip.SkipSink>
{
private readonly IObservable<TSource> _source;
private readonly int _count;
private readonly int _skip;
public CountSkip(IObservable<TSource> source, int count, int skip)
{
_source = source;
_count = count;
_skip = skip;
}
protected override SkipSink CreateSink(IObserver<IList<TSource>> observer) => new(observer, _count, _skip);
protected override void Run(SkipSink sink) => sink.Run(_source);
internal sealed class SkipSink : Sink<TSource, IList<TSource>>
{
private readonly int _count;
private readonly int _skip;
private int _index;
private IList<TSource>? _buffer;
internal SkipSink(IObserver<IList<TSource>> observer, int count, int skip) : base(observer)
{
_count = count;
_skip = skip;
}
public override void OnNext(TSource value)
{
var idx = _index;
var buffer = _buffer;
if (idx == 0)
{
buffer = new List<TSource>();
_buffer = buffer;
}
buffer?.Add(value);
if (++idx == _count)
{
_buffer = null;
ForwardOnNext(buffer!); // NB: Counting logic with _index ensures non-null.
}
if (idx == _skip)
{
_index = 0;
}
else
{
_index = idx;
}
}
public override void OnError(Exception error)
{
_buffer = null;
ForwardOnError(error);
}
public override void OnCompleted()
{
var buffer = _buffer;
_buffer = null;
if (buffer != null)
{
ForwardOnNext(buffer);
}
ForwardOnCompleted();
}
}
}
internal sealed class CountOverlap : Producer<IList<TSource>, CountOverlap.OverlapSink>
{
private readonly IObservable<TSource> _source;
private readonly int _count;
private readonly int _skip;
public CountOverlap(IObservable<TSource> source, int count, int skip)
{
_source = source;
_count = count;
_skip = skip;
}
protected override OverlapSink CreateSink(IObserver<IList<TSource>> observer) => new(observer, _count, _skip);
protected override void Run(OverlapSink sink) => sink.Run(_source);
internal sealed class OverlapSink : Sink<TSource, IList<TSource>>
{
private readonly Queue<IList<TSource>> _queue;
private readonly int _count;
private readonly int _skip;
private int _n;
public OverlapSink(IObserver<IList<TSource>> observer, int count, int skip)
: base(observer)
{
_queue = new Queue<IList<TSource>>();
_count = count;
_skip = skip;
CreateWindow();
}
private void CreateWindow()
{
var s = new List<TSource>();
_queue.Enqueue(s);
}
public override void OnNext(TSource value)
{
foreach (var s in _queue)
{
s.Add(value);
}
var c = _n - _count + 1;
if (c >= 0 && c % _skip == 0)
{
var s = _queue.Dequeue();
if (s.Count > 0)
{
ForwardOnNext(s);
}
}
_n++;
if (_n % _skip == 0)
{
CreateWindow();
}
}
public override void OnError(Exception error)
{
// just drop the ILists on the GC floor, no reason to clear them
_queue.Clear();
ForwardOnError(error);
}
public override void OnCompleted()
{
while (_queue.Count > 0)
{
var s = _queue.Dequeue();
if (s.Count > 0)
{
ForwardOnNext(s);
}
}
ForwardOnCompleted();
}
}
}
internal sealed class TimeSliding : Producer<IList<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<IList<TSource>> observer) => new(this, observer);
protected override void Run(_ sink) => sink.Run(this);
internal sealed class _ : Sink<TSource, IList<TSource>>
{
private readonly TimeSpan _timeShift;
private readonly IScheduler _scheduler;
private readonly object _gate = new();
private readonly Queue<List<TSource>> _q = new();
private SerialDisposableValue _timerSerial;
public _(TimeSliding parent, IObserver<IList<TSource>> observer)
: base(observer)
{
_timeShift = parent._timeShift;
_scheduler = parent._scheduler;
}
private TimeSpan _totalTime;
private TimeSpan _nextShift;
private TimeSpan _nextSpan;
public void Run(TimeSliding parent)
{
_totalTime = TimeSpan.Zero;
_nextShift = parent._timeShift;
_nextSpan = parent._timeSpan;
CreateWindow();
CreateTimer();
Run(parent._source);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_timerSerial.Dispose();
}
base.Dispose(disposing);
}
private void CreateWindow()
{
var s = new List<TSource>();
_q.Enqueue(s);
}
private void CreateTimer()
{
var m = new SingleAssignmentDisposable();
_timerSerial.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)
{
//
// Before v2, the two operations below were reversed. This doesn't have an observable
// difference for Buffer, but is done to keep code consistent with Window, where we
// took a breaking change in v2 to ensure consistency across overloads. For more info,
// see the comment in Tick for Window.
//
if (isSpan)
{
if (_q.Count > 0)
{
var s = _q.Dequeue();
ForwardOnNext(s);
}
}
if (isShift)
{
CreateWindow();
}
}
CreateTimer();
}
public override void OnNext(TSource value)
{
lock (_gate)
{
foreach (var s in _q)
{
s.Add(value);
}
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
while (_q.Count > 0)
{
_q.Dequeue().Clear();
}
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
while (_q.Count > 0)
{
ForwardOnNext(_q.Dequeue());
}
ForwardOnCompleted();
}
}
}
}
internal sealed class TimeHopping : Producer<IList<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<IList<TSource>> observer) => new(observer);
protected override void Run(_ sink) => sink.Run(this);
internal sealed class _ : Sink<TSource, IList<TSource>>
{
private readonly object _gate = new();
private List<TSource> _list = new();
public _(IObserver<IList<TSource>> observer)
: base(observer)
{
}
private SingleAssignmentDisposableValue _periodicDisposable;
public void Run(TimeHopping parent)
{
_periodicDisposable.Disposable = parent._scheduler.SchedulePeriodic(this, parent._timeSpan, static @this => @this.Tick());
Run(parent._source);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_periodicDisposable.Dispose();
}
base.Dispose(disposing);
}
private void Tick()
{
lock (_gate)
{
ForwardOnNext(_list);
_list = new List<TSource>();
}
}
public override void OnNext(TSource value)
{
lock (_gate)
{
_list.Add(value);
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
_list.Clear();
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
ForwardOnNext(_list);
ForwardOnCompleted();
}
}
}
}
internal sealed class Ferry : Producer<IList<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<IList<TSource>> observer) => new(this, observer);
protected override void Run(_ sink) => sink.Run();
internal sealed class _ : Sink<TSource, IList<TSource>>
{
private readonly Ferry _parent;
private readonly object _gate = new();
private List<TSource> _s = new();
public _(Ferry parent, IObserver<IList<TSource>> observer)
: base(observer)
{
_parent = parent;
}
private SerialDisposableValue _timerSerial;
private int _n;
private int _windowId;
public void Run()
{
_n = 0;
_windowId = 0;
CreateTimer(0);
SetUpstream(_parent._source.SubscribeSafe(this));
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_timerSerial.Dispose();
}
base.Dispose(disposing);
}
private void CreateTimer(int id)
{
var m = new SingleAssignmentDisposable();
_timerSerial.Disposable = m;
m.Disposable = _parent._scheduler.ScheduleAction((@this: this, id), _parent._timeSpan, static tuple => tuple.@this.Tick(tuple.id));
}
private void Tick(int id)
{
lock (_gate)
{
if (id != _windowId)
{
return;
}
_n = 0;
var newId = ++_windowId;
var res = _s;
_s = new List<TSource>();
ForwardOnNext(res);
CreateTimer(newId);
}
}
public override void OnNext(TSource value)
{
var newWindow = false;
var newId = 0;
lock (_gate)
{
_s.Add(value);
_n++;
if (_n == _parent._count)
{
newWindow = true;
_n = 0;
newId = ++_windowId;
var res = _s;
_s = new List<TSource>();
ForwardOnNext(res);
}
if (newWindow)
{
CreateTimer(newId);
}
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
_s.Clear();
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
ForwardOnNext(_s);
ForwardOnCompleted();
}
}
}
}
}
internal static class Buffer<TSource, TBufferClosing>
{
internal sealed class Selector : Producer<IList<TSource>, Selector._>
{
private readonly IObservable<TSource> _source;
private readonly Func<IObservable<TBufferClosing>> _bufferClosingSelector;
public Selector(IObservable<TSource> source, Func<IObservable<TBufferClosing>> bufferClosingSelector)
{
_source = source;
_bufferClosingSelector = bufferClosingSelector;
}
protected override _ CreateSink(IObserver<IList<TSource>> observer) => new(this, observer);
protected override void Run(_ sink) => sink.Run(_source);
internal sealed class _ : Sink<TSource, IList<TSource>>
{
private readonly object _gate = new();
private readonly AsyncLock _bufferGate = new();
private readonly Func<IObservable<TBufferClosing>> _bufferClosingSelector;
private List<TSource> _buffer = new();
private SerialDisposableValue _bufferClosingSerialDisposable;
public _(Selector parent, IObserver<IList<TSource>> observer)
: base(observer)
{
_bufferClosingSelector = parent._bufferClosingSelector;
}
public override void Run(IObservable<TSource> source)
{
base.Run(source);
_bufferGate.Wait(this, static @this => @this.CreateBufferClose());
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_bufferClosingSerialDisposable.Dispose();
}
base.Dispose(disposing);
}
private void CreateBufferClose()
{
IObservable<TBufferClosing> bufferClose;
try
{
bufferClose = _bufferClosingSelector();
}
catch (Exception exception)
{
lock (_gate)
{
ForwardOnError(exception);
}
return;
}
var closingObserver = new BufferClosingObserver(this);
_bufferClosingSerialDisposable.Disposable = closingObserver;
closingObserver.SetResource(bufferClose.SubscribeSafe(closingObserver));
}
private void CloseBuffer(IDisposable closingSubscription)
{
closingSubscription.Dispose();
lock (_gate)
{
var res = _buffer;
_buffer = new List<TSource>();
ForwardOnNext(res);
}
_bufferGate.Wait(this, static @this => @this.CreateBufferClose());
}
private sealed class BufferClosingObserver : SafeObserver<TBufferClosing>
{
private readonly _ _parent;
public BufferClosingObserver(_ parent)
{
_parent = parent;
}
public override void OnNext(TBufferClosing value)
{
_parent.CloseBuffer(this);
}
public override void OnError(Exception error)
{
_parent.OnError(error);
}
public override void OnCompleted()
{
_parent.CloseBuffer(this);
}
}
public override void OnNext(TSource value)
{
lock (_gate)
{
_buffer.Add(value);
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
_buffer.Clear();
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
ForwardOnNext(_buffer);
ForwardOnCompleted();
}
}
}
}
internal sealed class Boundaries : Producer<IList<TSource>, Boundaries._>
{
private readonly IObservable<TSource> _source;
private readonly IObservable<TBufferClosing> _bufferBoundaries;
public Boundaries(IObservable<TSource> source, IObservable<TBufferClosing> bufferBoundaries)
{
_source = source;
_bufferBoundaries = bufferBoundaries;
}
protected override _ CreateSink(IObserver<IList<TSource>> observer) => new(observer);
protected override void Run(_ sink) => sink.Run(this);
internal sealed class _ : Sink<TSource, IList<TSource>>
{
private readonly object _gate = new();
private List<TSource> _buffer = new();
private SingleAssignmentDisposableValue _boundariesDisposable;
public _(IObserver<IList<TSource>> observer)
: base(observer)
{
}
public void Run(Boundaries parent)
{
Run(parent._source);
_boundariesDisposable.Disposable = parent._bufferBoundaries.SubscribeSafe(new BufferClosingObserver(this));
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_boundariesDisposable.Dispose();
}
base.Dispose(disposing);
}
private sealed class BufferClosingObserver : IObserver<TBufferClosing>
{
private readonly _ _parent;
public BufferClosingObserver(_ parent)
{
_parent = parent;
}
public void OnNext(TBufferClosing value)
{
lock (_parent._gate)
{
var res = _parent._buffer;
_parent._buffer = new List<TSource>();
_parent.ForwardOnNext(res);
}
}
public void OnError(Exception error)
{
_parent.OnError(error);
}
public void OnCompleted()
{
_parent.OnCompleted();
}
}
public override void OnNext(TSource value)
{
lock (_gate)
{
_buffer.Add(value);
}
}
public override void OnError(Exception error)
{
lock (_gate)
{
_buffer.Clear();
ForwardOnError(error);
}
}
public override void OnCompleted()
{
lock (_gate)
{
ForwardOnNext(_buffer);
ForwardOnCompleted();
}
}
}
}
}
}