public static IAsyncEnumerable <TSource> Except <TSource>(this IAsyncEnumerable <TSource> first, IAsyncEnumerable <TSource> second, IEqualityComparer <TSource> comparer) { if (first == null) { throw new ArgumentNullException(nameof(first)); } if (second == null) { throw new ArgumentNullException(nameof(second)); } if (comparer == null) { throw new ArgumentNullException(nameof(comparer)); } return(CreateEnumerable( () => { var e = first.GetEnumerator(); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); var mapTask = default(Task <Dictionary <TSource, TSource> >); var getMapTask = new Func <CancellationToken, Task <Dictionary <TSource, TSource> > >( ct => mapTask ?? (mapTask = second.ToDictionary(x => x, comparer, ct))); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (await e.MoveNext(ct) .Zip(getMapTask(ct), (b, _) => b) .ConfigureAwait(false)) { if (!mapTask.Result.ContainsKey(e.Current)) { return true; } return await f(ct) .ConfigureAwait(false); } return false; }; return CreateEnumerator( f, () => e.Current, d.Dispose, e ); })); }
private static IAsyncEnumerable <TSource> Concat_ <TSource>(this IEnumerable <IAsyncEnumerable <TSource> > sources) { return(CreateEnumerable( () => { var se = sources.GetEnumerator(); var e = default(IAsyncEnumerator <TSource>); var cts = new CancellationTokenDisposable(); var a = new AssignableDisposable(); var d = Disposable.Create(cts, se, a); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (e == null) { var b = false; b = se.MoveNext(); if (b) { e = se.Current.GetEnumerator(); } if (!b) { return false; } a.Disposable = e; } if (await e.MoveNext(ct) .ConfigureAwait(false)) { return true; } e.Dispose(); e = null; return await f(ct) .ConfigureAwait(false); }; return CreateEnumerator( f, () => e.Current, d.Dispose, a ); })); }
public static IAsyncEnumerable <TSource> SkipWhile <TSource>(this IAsyncEnumerable <TSource> source, Func <TSource, int, bool> predicate) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var skipping = true; var index = 0; var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (skipping) { if (await e.MoveNext(ct) .ConfigureAwait(false)) { if (predicate(e.Current, checked (index++))) { return await f(ct) .ConfigureAwait(false); } skipping = false; return true; } return false; } return await e.MoveNext(ct) .ConfigureAwait(false); }; return CreateEnumerator( f, () => e.Current, d.Dispose, e ); })); }
public static IAsyncEnumerable <TSource> SkipLast <TSource>(this IAsyncEnumerable <TSource> source, int count) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); var q = new Queue <TSource>(); var current = default(TSource); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (await e.MoveNext(ct) .ConfigureAwait(false)) { var item = e.Current; q.Enqueue(item); if (q.Count > count) { current = q.Dequeue(); return true; } return await f(ct) .ConfigureAwait(false); } return false; }; return CreateEnumerator( f, () => current, d.Dispose, e ); })); }
public IDisposable Subscribe(IObserver <T> observer) { var ctd = new CancellationTokenDisposable(); var e = _source.GetAsyncEnumerator(ctd.Token); async void Core() { bool hasNext; try { hasNext = await e.MoveNextAsync().ConfigureAwait(false); } catch (Exception ex) { if (!ctd.Token.IsCancellationRequested) { observer.OnError(ex); await e.DisposeAsync().ConfigureAwait(false); } return; } if (hasNext) { observer.OnNext(e.Current); if (!ctd.Token.IsCancellationRequested) { Core(); } // In case cancellation is requested, this could only have happened // by disposing the returned composite disposable (see below). // In that case, e will be disposed too, so there is no need to dispose e here. } else { observer.OnCompleted(); await e.DisposeAsync().ConfigureAwait(false); } } Core(); // REVIEW: Safety of concurrent dispose operation; fire-and-forget nature of dispose? return(Disposable.Create(ctd, Disposable.Create(() => { e.DisposeAsync(); }))); }
private static IAsyncEnumerable <TSource> DoHelper <TSource>(this IAsyncEnumerable <TSource> source, Action <TSource> onNext, Action <Exception> onError, Action onCompleted) { return(CreateEnumerable( () => { var e = source.GetEnumerator(); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); var current = default(TSource); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { try { var result = await e.MoveNext(ct) .ConfigureAwait(false); if (!result) { onCompleted(); } else { current = e.Current; onNext(current); } return result; } catch (OperationCanceledException) { throw; } catch (Exception ex) { onError(ex); throw; } }; return CreateEnumerator( f, () => current, d.Dispose, e ); })); }
public static IAsyncEnumerable <TSource> DefaultIfEmpty <TSource>(this IAsyncEnumerable <TSource> source, TSource defaultValue) { if (source == null) { throw new ArgumentNullException(nameof(source)); } return(CreateEnumerable( () => { var done = false; var hasElements = false; var e = source.GetEnumerator(); var current = default(TSource); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (done) { return false; } if (await e.MoveNext(ct) .ConfigureAwait(false)) { hasElements = true; current = e.Current; return true; } done = true; if (!hasElements) { current = defaultValue; return true; } return false; }; return CreateEnumerator( f, () => current, d.Dispose, e ); })); }
private IAsyncEnumerable <IEnumerable <T> > Classes() { return(CreateEnumerable(() => { var e = equivalenceClasses.GetEnumerator(); var list = new List <IEnumerable <T> >(); var e1 = default(IEnumerator <IEnumerable <T> >); var cts = new CancellationTokenDisposable(); var d1 = new AssignableDisposable(); var d = Disposable.Create(cts, e, d1); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (await e.MoveNext(ct) .ConfigureAwait(false)) { list.AddRange(e.Current.OrderBy(keySelector, comparer) .GroupUntil(keySelector, x => x, comparer)); return await f(ct) .ConfigureAwait(false); } e.Dispose(); e1 = list.GetEnumerator(); d1.Disposable = e1; return e1.MoveNext(); }; return CreateEnumerator( async ct => { if (e1 != null) { return e1.MoveNext(); } return await f(cts.Token) .ConfigureAwait(false); }, () => e1.Current, d.Dispose, e ); })); }
public IDisposable Subscribe(IObserver <T> observer) { var ctd = new CancellationTokenDisposable(); async void Core() { await using (var e = _source.GetAsyncEnumerator(ctd.Token)) { do { bool hasNext; var value = default(T) !; try { hasNext = await e.MoveNextAsync().ConfigureAwait(false); if (hasNext) { value = e.Current; } } catch (Exception ex) { if (!ctd.Token.IsCancellationRequested) { observer.OnError(ex); } return; } if (!hasNext) { observer.OnCompleted(); return; } observer.OnNext(value); }while (!ctd.Token.IsCancellationRequested); } } // Fire and forget Core(); return(ctd); }
public static IAsyncEnumerable <TResult> Zip <TFirst, TSecond, TResult>(this IAsyncEnumerable <TFirst> first, IAsyncEnumerable <TSecond> second, Func <TFirst, TSecond, TResult> selector) { if (first == null) { throw new ArgumentNullException("first"); } if (second == null) { throw new ArgumentNullException("second"); } if (selector == null) { throw new ArgumentNullException("selector"); } return(Create(() => { var e1 = first.GetEnumerator(); var e2 = second.GetEnumerator(); var current = default(TResult); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e1, e2); return Create( (ct, tcs) => { e1.MoveNext(cts.Token).Zip(e2.MoveNext(cts.Token), (f, s) => { var result = f && s; if (result) { current = selector(e1.Current, e2.Current); } return result; }).Then(t => { t.Handle(tcs, x => tcs.TrySetResult(x)); }); return tcs.Task.UsingEnumerator(e1).UsingEnumerator(e2); }, () => current, d.Dispose ); })); }
public static IAsyncEnumerable <TAccumulate> Scan <TSource, TAccumulate>(this IAsyncEnumerable <TSource> source, TAccumulate seed, Func <TAccumulate, TSource, TAccumulate> accumulator) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (accumulator == null) { throw new ArgumentNullException(nameof(accumulator)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); var acc = seed; var current = default(TAccumulate); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (!await e.MoveNext(ct) .ConfigureAwait(false)) { return false; } var item = e.Current; acc = accumulator(acc, item); current = acc; return true; }; return CreateEnumerator( f, () => current, d.Dispose, e ); })); }
public static IAsyncEnumerable <TSource> Skip <TSource>(this IAsyncEnumerable <TSource> source, int count) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var n = count; var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { var moveNext = await e.MoveNext(ct) .ConfigureAwait(false); if (n == 0) { return moveNext; } --n; if (!moveNext) { return false; } return await f(ct) .ConfigureAwait(false); }; return CreateEnumerator( ct => f(cts.Token), () => e.Current, d.Dispose, e ); })); }
public static IAsyncEnumerable <TSource> Repeat <TSource>(this IAsyncEnumerable <TSource> source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } return(CreateEnumerable( () => { var e = default(IAsyncEnumerator <TSource>); var a = new AssignableDisposable(); var current = default(TSource); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, a); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (e == null) { e = source.GetEnumerator(); a.Disposable = e; } if (await e.MoveNext(ct) .ConfigureAwait(false)) { current = e.Current; return true; } e = null; return await f(ct) .ConfigureAwait(false); }; return CreateEnumerator( f, () => current, d.Dispose, e ); })); }
public static IAsyncEnumerable <TSource> Take <TSource>(this IAsyncEnumerable <TSource> source, int count) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var n = count; var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); return CreateEnumerator( async ct => { if (n == 0) { return false; } var result = await e.MoveNext(cts.Token) .ConfigureAwait(false); --n; if (n == 0) { e.Dispose(); } return result; }, () => e.Current, d.Dispose, e ); })); }
public IDisposable Subscribe(IObserver <T> observer) { var ctd = new CancellationTokenDisposable(); var e = source.GetEnumerator(); var f = default(Action); f = () => e.MoveNext(ctd.Token) .ContinueWith(t => { if (t.IsFaulted) { observer.OnError(t.Exception); e.Dispose(); } else if (t.IsCanceled) { e.Dispose(); } else if (t.IsCompleted) { if (t.Result) { observer.OnNext(e.Current); if (!ctd.Token.IsCancellationRequested) { f(); } //In case cancellation is requested, this could only have happened //by disposing the returned composite disposable (see below). //In that case, e will be disposed too, so there is no need to dispose e here. } else { observer.OnCompleted(); e.Dispose(); } } }, ctd.Token); f(); return(Disposable.Create(ctd, e)); }
public IAsyncEnumerator <TElement> GetEnumerator() { var index = -1; var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, sourceDisposable); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { var size = 0; lock (elements) size = elements.Count; if (index < size) { return(true); } if (done) { exception?.Throw(); return(false); } if (await iterateSource(ct) .ConfigureAwait(false)) { return(await f(ct) .ConfigureAwait(false)); } return(false); }; return(CreateEnumerator( ct => { ++index; return f(cts.Token); }, () => elements[index], d.Dispose, null )); }
public static IAsyncEnumerable <TResult> Zip <TFirst, TSecond, TResult>(this IAsyncEnumerable <TFirst> first, IAsyncEnumerable <TSecond> second, Func <TFirst, TSecond, TResult> selector) { if (first == null) { throw new ArgumentNullException(nameof(first)); } if (second == null) { throw new ArgumentNullException(nameof(second)); } if (selector == null) { throw new ArgumentNullException(nameof(selector)); } return(CreateEnumerable( () => { var e1 = first.GetEnumerator(); var e2 = second.GetEnumerator(); var current = default(TResult); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e1, e2); return CreateEnumerator( ct => e1.MoveNext(cts.Token) .Zip(e2.MoveNext(cts.Token), (f, s) => { var result = f && s; if (result) { current = selector(e1.Current, e2.Current); } return result; }), () => current, d.Dispose ); })); }
public static IAsyncEnumerable <TSource> Finally <TSource>(this IAsyncEnumerable <TSource> source, Action finallyAction) { if (source == null) { throw new ArgumentNullException("source"); } if (finallyAction == null) { throw new ArgumentNullException("finallyAction"); } return(Create(() => { var e = source.GetEnumerator(); var cts = new CancellationTokenDisposable(); var r = new Disposable(finallyAction); var d = Disposable.Create(cts, e, r); var f = default(Action <TaskCompletionSource <bool>, CancellationToken>); f = (tcs, ct) => { e.MoveNext(ct).Then(t => { t.Handle(tcs, res => { tcs.TrySetResult(res); }); }); }; return Create( (ct, tcs) => { f(tcs, cts.Token); return tcs.Task.UsingEnumeratorSync(r); }, () => e.Current, d.Dispose ); })); }
public static IAsyncEnumerable <TSource> Reverse <TSource>(this IAsyncEnumerable <TSource> source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var stack = default(Stack <TSource>); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); return CreateEnumerator( async ct => { if (stack == null) { stack = await CreateEnumerable( () => e) .Aggregate(new Stack <TSource>(), (s, x) => { s.Push(x); return s; }, cts.Token) .ConfigureAwait(false); return stack.Count > 0; } stack.Pop(); return stack.Count > 0; }, () => stack.Peek(), d.Dispose, e ); })); }
public static IAsyncEnumerable <TResult> Select <TSource, TResult>(this IAsyncEnumerable <TSource> source, Func <TSource, int, TResult> selector) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (selector == null) { throw new ArgumentNullException(nameof(selector)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var current = default(TResult); var index = 0; var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); return CreateEnumerator( async ct => { if (await e.MoveNext(cts.Token) .ConfigureAwait(false)) { current = selector(e.Current, checked (index++)); return true; } return false; }, () => current, d.Dispose, e ); })); }
public IDisposable Subscribe(IObserver <T> observer) { var ctd = new CancellationTokenDisposable(); var e = source.GetEnumerator(); var f = default(Action); f = () => e.MoveNext(ctd.Token).ContinueWith(t => { if (t.IsFaulted) { observer.OnError(t.Exception); e.Dispose(); } else if (t.IsCanceled) { e.Dispose(); } else if (t.IsCompleted) { if (t.Result) { observer.OnNext(e.Current); f(); } else { observer.OnCompleted(); e.Dispose(); } } }, ctd.Token); f(); return(Disposable.Create(ctd, e)); }
public static IAsyncEnumerable <TSource> TakeWhile <TSource>(this IAsyncEnumerable <TSource> source, Func <TSource, int, bool> predicate) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var index = 0; var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); return CreateEnumerator( async ct => { if (await e.MoveNext(cts.Token) .ConfigureAwait(false)) { return predicate(e.Current, checked (index++)); } return false; }, () => e.Current, d.Dispose, e ); })); }
public static IAsyncEnumerable <TSource> IgnoreElements <TSource>(this IAsyncEnumerable <TSource> source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, e); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (!await e.MoveNext(ct) .ConfigureAwait(false)) { return false; } return await f(ct) .ConfigureAwait(false); }; return CreateEnumerator <TSource>( f, () => { throw new InvalidOperationException(); }, d.Dispose, e ); })); }
public static IAsyncEnumerable <TSource> Using <TSource, TResource>(Func <TResource> resourceFactory, Func <TResource, IAsyncEnumerable <TSource> > enumerableFactory) where TResource : IDisposable { if (resourceFactory == null) { throw new ArgumentNullException("resourceFactory"); } if (enumerableFactory == null) { throw new ArgumentNullException("enumerableFactory"); } return(Create(() => { var resource = resourceFactory(); var e = default(IAsyncEnumerator <TSource>); try { e = enumerableFactory(resource).GetEnumerator(); } catch (Exception) { resource.Dispose(); throw; } var cts = new CancellationTokenDisposable(); var d = new CompositeDisposable(cts, resource, e); var current = default(TSource); return Create( (ct, tcs) => { e.MoveNext(cts.Token).ContinueWith(t => { t.Handle(tcs, res => { if (res) { current = e.Current; tcs.TrySetResult(true); } else { d.Dispose(); tcs.TrySetResult(false); } }, ex => { d.Dispose(); tcs.TrySetException(ex); } ); }); return tcs.Task; }, () => current, d.Dispose ); })); }
public static IAsyncEnumerable <IAsyncGrouping <TKey, TElement> > GroupBy <TSource, TKey, TElement>(this IAsyncEnumerable <TSource> source, Func <TSource, TKey> keySelector, Func <TSource, TElement> elementSelector, IEqualityComparer <TKey> comparer) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (keySelector == null) { throw new ArgumentNullException(nameof(keySelector)); } if (elementSelector == null) { throw new ArgumentNullException(nameof(elementSelector)); } if (comparer == null) { throw new ArgumentNullException(nameof(comparer)); } return(CreateEnumerable(() => { var gate = new object(); var e = source.GetEnumerator(); var count = 1; var map = new Dictionary <TKey, AsyncGrouping <TKey, TElement> >(comparer); var list = new List <IAsyncGrouping <TKey, TElement> >(); var index = 0; var current = default(IAsyncGrouping <TKey, TElement>); var faulted = default(ExceptionDispatchInfo); var res = default(bool?); var cts = new CancellationTokenDisposable(); var refCount = new Disposable( () => { if (Interlocked.Decrement(ref count) == 0) { e.Dispose(); } } ); var d = Disposable.Create(cts, refCount); var iterateSource = default(Func <CancellationToken, Task <bool> >); iterateSource = async ct => { lock (gate) { if (res != null) { return res.Value; } res = null; } faulted?.Throw(); try { res = await e.MoveNext(ct) .ConfigureAwait(false); if (res == true) { var key = default(TKey); var element = default(TElement); var cur = e.Current; try { key = keySelector(cur); element = elementSelector(cur); } catch (Exception exception) { foreach (var v in map.Values) { v.Error(exception); } throw; } var group = default(AsyncGrouping <TKey, TElement>); if (!map.TryGetValue(key, out group)) { group = new AsyncGrouping <TKey, TElement>(key, iterateSource, refCount); map.Add(key, group); lock (list) list.Add(group); Interlocked.Increment(ref count); } group.Add(element); } return res.Value; } catch (Exception ex) { foreach (var v in map.Values) { v.Error(ex); } faulted = ExceptionDispatchInfo.Capture(ex); throw; } finally { res = null; } }; var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { var result = await iterateSource(ct) .ConfigureAwait(false); current = null; lock (list) { if (index < list.Count) { current = list[index++]; } } if (current != null) { return true; } return result && await f(ct) .ConfigureAwait(false); }; return CreateEnumerator( f, () => current, d.Dispose, e ); })); }
private static IAsyncEnumerable <TSource> OnErrorResumeNext_ <TSource>(IEnumerable <IAsyncEnumerable <TSource> > sources) { return(Create(() => { var se = sources.GetEnumerator(); var e = default(IAsyncEnumerator <TSource>); var cts = new CancellationTokenDisposable(); var a = new AssignableDisposable(); var d = Disposable.Create(cts, se, a); var f = default(Action <TaskCompletionSource <bool>, CancellationToken>); f = (tcs, ct) => { if (e == null) { var b = false; try { b = se.MoveNext(); if (b) { e = se.Current.GetEnumerator(); } } catch (Exception ex) { tcs.TrySetException(ex); return; } if (!b) { tcs.TrySetResult(false); return; } a.Disposable = e; } e.MoveNext(ct).Then(t => { t.Handle(tcs, res => { if (res) { tcs.TrySetResult(true); } else { e.Dispose(); e = null; f(tcs, ct); } }, ex => { e.Dispose(); e = null; f(tcs, ct); } ); }); }; return Create( (ct, tcs) => { f(tcs, cts.Token); return tcs.Task.UsingEnumerator(a); }, () => e.Current, d.Dispose ); })); }
public static IAsyncEnumerable <TSource> Catch <TSource, TException>(this IAsyncEnumerable <TSource> source, Func <TException, IAsyncEnumerable <TSource> > handler) where TException : Exception { if (source == null) { throw new ArgumentNullException("source"); } if (handler == null) { throw new ArgumentNullException("handler"); } return(Create(() => { var e = source.GetEnumerator(); var cts = new CancellationTokenDisposable(); var a = new AssignableDisposable { Disposable = e }; var d = Disposable.Create(cts, a); var done = false; var f = default(Action <TaskCompletionSource <bool>, CancellationToken>); f = (tcs, ct) => { if (!done) { e.MoveNext(ct).Then(t => { t.Handle(tcs, res => { tcs.TrySetResult(res); }, ex => { var err = default(IAsyncEnumerator <TSource>); try { ex.Flatten().Handle(ex_ => { var exx = ex_ as TException; if (exx != null) { err = handler(exx).GetEnumerator(); return true; } return false; }); } catch (Exception ex2) { tcs.TrySetException(ex2); return; } if (err != null) { e = err; a.Disposable = e; done = true; f(tcs, ct); } } ); }); } else { e.MoveNext(ct).Then(t => { t.Handle(tcs, res => { tcs.TrySetResult(res); }); }); } }; return Create( (ct, tcs) => { f(tcs, cts.Token); return tcs.Task.UsingEnumerator(a); }, () => e.Current, d.Dispose ); })); }
public static IAsyncEnumerable <TSource> Expand <TSource>(this IAsyncEnumerable <TSource> source, Func <TSource, IAsyncEnumerable <TSource> > selector) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (selector == null) { throw new ArgumentNullException(nameof(selector)); } return(CreateEnumerable( () => { var e = default(IAsyncEnumerator <TSource>); var cts = new CancellationTokenDisposable(); var a = new AssignableDisposable(); var d = Disposable.Create(cts, a); var queue = new Queue <IAsyncEnumerable <TSource> >(); queue.Enqueue(source); var current = default(TSource); var f = default(Func <CancellationToken, Task <bool> >); f = async ct => { if (e == null) { if (queue.Count > 0) { var src = queue.Dequeue(); e = src.GetEnumerator(); a.Disposable = e; return await f(ct) .ConfigureAwait(false); } return false; } if (await e.MoveNext(ct) .ConfigureAwait(false)) { var item = e.Current; var next = selector(item); queue.Enqueue(next); current = item; return true; } e = null; return await f(ct) .ConfigureAwait(false); }; return CreateEnumerator( f, () => current, d.Dispose, e ); })); }
public static IAsyncEnumerable <TResult> Join <TOuter, TInner, TKey, TResult>(this IAsyncEnumerable <TOuter> outer, IAsyncEnumerable <TInner> inner, Func <TOuter, TKey> outerKeySelector, Func <TInner, TKey> innerKeySelector, Func <TOuter, TInner, TResult> resultSelector, IEqualityComparer <TKey> comparer) { if (outer == null) { throw new ArgumentNullException("outer"); } if (inner == null) { throw new ArgumentNullException("inner"); } if (outerKeySelector == null) { throw new ArgumentNullException("outerKeySelector"); } if (innerKeySelector == null) { throw new ArgumentNullException("innerKeySelector"); } if (resultSelector == null) { throw new ArgumentNullException("resultSelector"); } if (comparer == null) { throw new ArgumentNullException("comparer"); } return(Create(() => { var oe = outer.GetEnumerator(); var ie = inner.GetEnumerator(); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, oe, ie); var current = default(TResult); var useOuter = true; var outerMap = new Dictionary <TKey, List <TOuter> >(comparer); var innerMap = new Dictionary <TKey, List <TInner> >(comparer); var q = new Queue <TResult>(); var gate = new object(); var f = default(Action <TaskCompletionSource <bool>, CancellationToken>); f = (tcs, ct) => { if (q.Count > 0) { current = q.Dequeue(); tcs.TrySetResult(true); return; } var b = useOuter; if (ie == null && oe == null) { tcs.TrySetResult(false); return; } else if (ie == null) { b = true; } else if (oe == null) { b = false; } useOuter = !useOuter; var enqueue = new Func <TOuter, TInner, bool>((o, i) => { var result = default(TResult); try { result = resultSelector(o, i); } catch (Exception exception) { tcs.TrySetException(exception); return false; } q.Enqueue(result); return true; }); if (b) { oe.MoveNext(ct).Then(t => { t.Handle(tcs, res => { if (res) { var element = oe.Current; var key = default(TKey); try { key = outerKeySelector(element); } catch (Exception exception) { tcs.TrySetException(exception); return; } var outerList = default(List <TOuter>); if (!outerMap.TryGetValue(key, out outerList)) { outerList = new List <TOuter>(); outerMap.Add(key, outerList); } outerList.Add(element); var innerList = default(List <TInner>); if (!innerMap.TryGetValue(key, out innerList)) { innerList = new List <TInner>(); innerMap.Add(key, innerList); } foreach (var v in innerList) { if (!enqueue(element, v)) { return; } } f(tcs, ct); } else { oe.Dispose(); oe = null; f(tcs, ct); } }); }); } else { ie.MoveNext(ct).Then(t => { t.Handle(tcs, res => { if (res) { var element = ie.Current; var key = default(TKey); try { key = innerKeySelector(element); } catch (Exception exception) { tcs.TrySetException(exception); return; } var innerList = default(List <TInner>); if (!innerMap.TryGetValue(key, out innerList)) { innerList = new List <TInner>(); innerMap.Add(key, innerList); } innerList.Add(element); var outerList = default(List <TOuter>); if (!outerMap.TryGetValue(key, out outerList)) { outerList = new List <TOuter>(); outerMap.Add(key, outerList); } foreach (var v in outerList) { if (!enqueue(v, element)) { return; } } f(tcs, ct); } else { ie.Dispose(); ie = null; f(tcs, ct); } }); }); } }; return Create( (ct, tcs) => { f(tcs, cts.Token); return tcs.Task.UsingEnumerator(oe).UsingEnumerator(ie); }, () => current, d.Dispose ); })); }
public static IAsyncEnumerable <TResult> SelectMany <TSource, TResult>(this IAsyncEnumerable <TSource> source, Func <TSource, int, IAsyncEnumerable <TResult> > selector) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (selector == null) { throw new ArgumentNullException(nameof(selector)); } return(CreateEnumerable( () => { var e = source.GetEnumerator(); var ie = default(IAsyncEnumerator <TResult>); var index = 0; var innerDisposable = new AssignableDisposable(); var cts = new CancellationTokenDisposable(); var d = Disposable.Create(cts, innerDisposable, e); var inner = default(Func <CancellationToken, Task <bool> >); var outer = default(Func <CancellationToken, Task <bool> >); inner = async ct => { if (await ie.MoveNext(ct) .ConfigureAwait(false)) { return true; } innerDisposable.Disposable = null; return await outer(ct) .ConfigureAwait(false); }; outer = async ct => { if (await e.MoveNext(ct) .ConfigureAwait(false)) { var enumerable = selector(e.Current, checked (index++)); ie = enumerable.GetEnumerator(); innerDisposable.Disposable = ie; return await inner(ct) .ConfigureAwait(false); } return false; }; return CreateEnumerator(ct => ie == null ? outer(cts.Token) : inner(cts.Token), () => ie.Current, d.Dispose, e ); })); }