// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace System.Linq { public static partial class AsyncEnumerable { public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func> selector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (selector == null) throw Error.ArgumentNull(nameof(selector)); return new SelectManyAsyncIterator(source, selector); } public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func>> selector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (selector == null) throw Error.ArgumentNull(nameof(selector)); return new SelectManyAsyncIteratorWithTask(source, selector); } #if !NO_DEEP_CANCELLATION public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func>> selector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (selector == null) throw Error.ArgumentNull(nameof(selector)); return new SelectManyAsyncIteratorWithTaskAndCancellation(source, selector); } #endif public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func> selector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (selector == null) throw Error.ArgumentNull(nameof(selector)); #if USE_ASYNC_ITERATOR return Create(Core); async IAsyncEnumerator Core(CancellationToken cancellationToken) { int index = -1; await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { checked { index++; } var inner = selector(element, index); await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false)) { yield return subElement; } } } #else return new SelectManyWithIndexAsyncIterator(source, selector); #endif } public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func>> selector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (selector == null) throw Error.ArgumentNull(nameof(selector)); #if USE_ASYNC_ITERATOR return Create(Core); async IAsyncEnumerator Core(CancellationToken cancellationToken) { int index = -1; await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { checked { index++; } var inner = await selector(element, index).ConfigureAwait(false); await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false)) { yield return subElement; } } } #else return new SelectManyWithIndexAsyncIteratorWithTask(source, selector); #endif } #if !NO_DEEP_CANCELLATION public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func>> selector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (selector == null) throw Error.ArgumentNull(nameof(selector)); #if USE_ASYNC_ITERATOR return Create(Core); async IAsyncEnumerator Core(CancellationToken cancellationToken) { int index = -1; await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { checked { index++; } var inner = await selector(element, index, cancellationToken).ConfigureAwait(false); await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false)) { yield return subElement; } } } #else return new SelectManyWithIndexAsyncIteratorWithTaskAndCancellation(source, selector); #endif } #endif public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func> collectionSelector, Func resultSelector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (collectionSelector == null) throw Error.ArgumentNull(nameof(collectionSelector)); if (resultSelector == null) throw Error.ArgumentNull(nameof(resultSelector)); #if USE_ASYNC_ITERATOR return Create(Core); async IAsyncEnumerator Core(CancellationToken cancellationToken) { await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { var inner = collectionSelector(element); await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false)) { yield return resultSelector(element, subElement); } } } #else return new SelectManyAsyncIterator(source, collectionSelector, resultSelector); #endif } public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (collectionSelector == null) throw Error.ArgumentNull(nameof(collectionSelector)); if (resultSelector == null) throw Error.ArgumentNull(nameof(resultSelector)); #if USE_ASYNC_ITERATOR return Create(Core); async IAsyncEnumerator Core(CancellationToken cancellationToken) { await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { var inner = await collectionSelector(element).ConfigureAwait(false); await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false)) { yield return await resultSelector(element, subElement).ConfigureAwait(false); } } } #else return new SelectManyAsyncIteratorWithTask(source, collectionSelector, resultSelector); #endif } #if !NO_DEEP_CANCELLATION public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (collectionSelector == null) throw Error.ArgumentNull(nameof(collectionSelector)); if (resultSelector == null) throw Error.ArgumentNull(nameof(resultSelector)); #if USE_ASYNC_ITERATOR return Create(Core); async IAsyncEnumerator Core(CancellationToken cancellationToken) { await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { var inner = await collectionSelector(element, cancellationToken).ConfigureAwait(false); await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false)) { yield return await resultSelector(element, subElement, cancellationToken).ConfigureAwait(false); } } } #else return new SelectManyAsyncIteratorWithTaskAndCancellation(source, collectionSelector, resultSelector); #endif } #endif public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func> collectionSelector, Func resultSelector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (collectionSelector == null) throw Error.ArgumentNull(nameof(collectionSelector)); if (resultSelector == null) throw Error.ArgumentNull(nameof(resultSelector)); #if USE_ASYNC_ITERATOR return Create(Core); async IAsyncEnumerator Core(CancellationToken cancellationToken) { int index = -1; await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { checked { index++; } var inner = collectionSelector(element, index); await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false)) { yield return resultSelector(element, subElement); } } } #else return new SelectManyWithIndexAsyncIterator(source, collectionSelector, resultSelector); #endif } public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (collectionSelector == null) throw Error.ArgumentNull(nameof(collectionSelector)); if (resultSelector == null) throw Error.ArgumentNull(nameof(resultSelector)); #if USE_ASYNC_ITERATOR return Create(Core); async IAsyncEnumerator Core(CancellationToken cancellationToken) { int index = -1; await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { checked { index++; } var inner = await collectionSelector(element, index).ConfigureAwait(false); await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false)) { yield return await resultSelector(element, subElement).ConfigureAwait(false); } } } #else return new SelectManyWithIndexAsyncIteratorWithTask(source, collectionSelector, resultSelector); #endif } #if !NO_DEEP_CANCELLATION public static IAsyncEnumerable SelectMany(this IAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { if (source == null) throw Error.ArgumentNull(nameof(source)); if (collectionSelector == null) throw Error.ArgumentNull(nameof(collectionSelector)); if (resultSelector == null) throw Error.ArgumentNull(nameof(resultSelector)); #if USE_ASYNC_ITERATOR return Create(Core); async IAsyncEnumerator Core(CancellationToken cancellationToken) { int index = -1; await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { checked { index++; } var inner = await collectionSelector(element, index, cancellationToken).ConfigureAwait(false); await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false)) { yield return await resultSelector(element, subElement, cancellationToken).ConfigureAwait(false); } } } #else return new SelectManyWithIndexAsyncIteratorWithTaskAndCancellation(source, collectionSelector, resultSelector); #endif } #endif private sealed class SelectManyAsyncIterator : AsyncIterator, IAsyncIListProvider { private const int State_Source = 1; private const int State_Result = 2; private readonly Func> _selector; private readonly IAsyncEnumerable _source; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyAsyncIterator(IAsyncEnumerable source, Func> selector) { Debug.Assert(source != null); Debug.Assert(selector != null); _source = source; _selector = selector; } public override AsyncIteratorBase Clone() { return new SelectManyAsyncIterator(_source, _selector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } await base.DisposeAsync().ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(cancellationToken); async ValueTask Core(CancellationToken _cancellationToken) { var count = 0; #if USE_AWAIT_FOREACH await foreach (var element in _source.WithCancellation(_cancellationToken).ConfigureAwait(false)) { checked { count += await _selector(element).CountAsync().ConfigureAwait(false); } } #else var e = _source.GetAsyncEnumerator(_cancellationToken); try { while (await e.MoveNextAsync().ConfigureAwait(false)) { checked { count += await _selector(e.Current).CountAsync().ConfigureAwait(false); } } } finally { await e.DisposeAsync().ConfigureAwait(false); } #endif return count; } } public async ValueTask ToArrayAsync(CancellationToken cancellationToken) { // REVIEW: Substitute for SparseArrayBuilder logic once we have access to that. var list = await ToListAsync(cancellationToken).ConfigureAwait(false); return list.ToArray(); } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var list = new List(); #if USE_AWAIT_FOREACH await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false)) { var items = _selector(element); await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false); } #else var e = _source.GetAsyncEnumerator(cancellationToken); try { while (await e.MoveNextAsync().ConfigureAwait(false)) { var items = _selector(e.Current); await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false); } } finally { await e.DisposeAsync().ConfigureAwait(false); } #endif return list; } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } var inner = _selector(_sourceEnumerator.Current); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = _resultEnumerator.Current; return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } private sealed class SelectManyAsyncIteratorWithTask : AsyncIterator, IAsyncIListProvider { private const int State_Source = 1; private const int State_Result = 2; private readonly Func>> _selector; private readonly IAsyncEnumerable _source; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyAsyncIteratorWithTask(IAsyncEnumerable source, Func>> selector) { Debug.Assert(source != null); Debug.Assert(selector != null); _source = source; _selector = selector; } public override AsyncIteratorBase Clone() { return new SelectManyAsyncIteratorWithTask(_source, _selector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } await base.DisposeAsync().ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(cancellationToken); async ValueTask Core(CancellationToken _cancellationToken) { var count = 0; #if USE_AWAIT_FOREACH await foreach (var element in _source.WithCancellation(_cancellationToken).ConfigureAwait(false)) { var items = await _selector(element).ConfigureAwait(false); checked { count += await items.CountAsync().ConfigureAwait(false); } } #else var e = _source.GetAsyncEnumerator(_cancellationToken); try { while (await e.MoveNextAsync().ConfigureAwait(false)) { var items = await _selector(e.Current).ConfigureAwait(false); checked { count += await items.CountAsync().ConfigureAwait(false); } } } finally { await e.DisposeAsync().ConfigureAwait(false); } #endif return count; } } public async ValueTask ToArrayAsync(CancellationToken cancellationToken) { // REVIEW: Substitute for SparseArrayBuilder logic once we have access to that. var list = await ToListAsync(cancellationToken).ConfigureAwait(false); return list.ToArray(); } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var list = new List(); #if USE_AWAIT_FOREACH await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false)) { var items = await _selector(element).ConfigureAwait(false); await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false); } #else var e = _source.GetAsyncEnumerator(cancellationToken); try { while (await e.MoveNextAsync().ConfigureAwait(false)) { var items = await _selector(e.Current).ConfigureAwait(false); await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false); } } finally { await e.DisposeAsync().ConfigureAwait(false); } #endif return list; } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } var inner = await _selector(_sourceEnumerator.Current).ConfigureAwait(false); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = _resultEnumerator.Current; return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } #if !NO_DEEP_CANCELLATION private sealed class SelectManyAsyncIteratorWithTaskAndCancellation : AsyncIterator, IAsyncIListProvider { private const int State_Source = 1; private const int State_Result = 2; private readonly Func>> _selector; private readonly IAsyncEnumerable _source; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable source, Func>> selector) { Debug.Assert(source != null); Debug.Assert(selector != null); _source = source; _selector = selector; } public override AsyncIteratorBase Clone() { return new SelectManyAsyncIteratorWithTaskAndCancellation(_source, _selector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } await base.DisposeAsync().ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(cancellationToken); async ValueTask Core(CancellationToken _cancellationToken) { var count = 0; #if USE_AWAIT_FOREACH await foreach (var element in _source.WithCancellation(_cancellationToken).ConfigureAwait(false)) { var items = await _selector(element, _cancellationToken).ConfigureAwait(false); checked { count += await items.CountAsync().ConfigureAwait(false); } } #else var e = _source.GetAsyncEnumerator(_cancellationToken); try { while (await e.MoveNextAsync().ConfigureAwait(false)) { var items = await _selector(e.Current, _cancellationToken).ConfigureAwait(false); checked { count += await items.CountAsync().ConfigureAwait(false); } } } finally { await e.DisposeAsync().ConfigureAwait(false); } #endif return count; } } public async ValueTask ToArrayAsync(CancellationToken cancellationToken) { // REVIEW: Substitute for SparseArrayBuilder logic once we have access to that. var list = await ToListAsync(cancellationToken).ConfigureAwait(false); return list.ToArray(); } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var list = new List(); #if USE_AWAIT_FOREACH await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false)) { var items = await _selector(element, cancellationToken).ConfigureAwait(false); await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false); } #else var e = _source.GetAsyncEnumerator(cancellationToken); try { while (await e.MoveNextAsync().ConfigureAwait(false)) { var items = await _selector(e.Current, cancellationToken).ConfigureAwait(false); await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false); } } finally { await e.DisposeAsync().ConfigureAwait(false); } #endif return list; } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } var inner = await _selector(_sourceEnumerator.Current, _cancellationToken).ConfigureAwait(false); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = _resultEnumerator.Current; return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } #endif #if !USE_ASYNC_ITERATOR private sealed class SelectManyAsyncIterator : AsyncIterator { private const int State_Source = 1; private const int State_Result = 2; private readonly Func> _collectionSelector; private readonly Func _resultSelector; private readonly IAsyncEnumerable _source; private TSource _currentSource; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyAsyncIterator(IAsyncEnumerable source, Func> collectionSelector, Func resultSelector) { Debug.Assert(source != null); Debug.Assert(collectionSelector != null); Debug.Assert(resultSelector != null); _source = source; _collectionSelector = collectionSelector; _resultSelector = resultSelector; } public override AsyncIteratorBase Clone() { return new SelectManyAsyncIterator(_source, _collectionSelector, _resultSelector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } _currentSource = default; await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } _currentSource = _sourceEnumerator.Current; var inner = _collectionSelector(_currentSource); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = _resultSelector(_currentSource, _resultEnumerator.Current); return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } private sealed class SelectManyAsyncIteratorWithTask : AsyncIterator { private const int State_Source = 1; private const int State_Result = 2; private readonly Func>> _collectionSelector; private readonly Func> _resultSelector; private readonly IAsyncEnumerable _source; private TSource _currentSource; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyAsyncIteratorWithTask(IAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { Debug.Assert(source != null); Debug.Assert(collectionSelector != null); Debug.Assert(resultSelector != null); _source = source; _collectionSelector = collectionSelector; _resultSelector = resultSelector; } public override AsyncIteratorBase Clone() { return new SelectManyAsyncIteratorWithTask(_source, _collectionSelector, _resultSelector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } _currentSource = default; await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } _currentSource = _sourceEnumerator.Current; var inner = await _collectionSelector(_currentSource).ConfigureAwait(false); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = await _resultSelector(_currentSource, _resultEnumerator.Current).ConfigureAwait(false); return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } #if !NO_DEEP_CANCELLATION private sealed class SelectManyAsyncIteratorWithTaskAndCancellation : AsyncIterator { private const int State_Source = 1; private const int State_Result = 2; private readonly Func>> _collectionSelector; private readonly Func> _resultSelector; private readonly IAsyncEnumerable _source; private TSource _currentSource; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { Debug.Assert(source != null); Debug.Assert(collectionSelector != null); Debug.Assert(resultSelector != null); _source = source; _collectionSelector = collectionSelector; _resultSelector = resultSelector; } public override AsyncIteratorBase Clone() { return new SelectManyAsyncIteratorWithTaskAndCancellation(_source, _collectionSelector, _resultSelector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } _currentSource = default; await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } _currentSource = _sourceEnumerator.Current; var inner = await _collectionSelector(_currentSource, _cancellationToken).ConfigureAwait(false); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = await _resultSelector(_currentSource, _resultEnumerator.Current, _cancellationToken).ConfigureAwait(false); return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } #endif private sealed class SelectManyWithIndexAsyncIterator : AsyncIterator { private const int State_Source = 1; private const int State_Result = 2; private readonly Func> _collectionSelector; private readonly Func _resultSelector; private readonly IAsyncEnumerable _source; private TSource _currentSource; private int _index; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyWithIndexAsyncIterator(IAsyncEnumerable source, Func> collectionSelector, Func resultSelector) { Debug.Assert(source != null); Debug.Assert(collectionSelector != null); Debug.Assert(resultSelector != null); _source = source; _collectionSelector = collectionSelector; _resultSelector = resultSelector; } public override AsyncIteratorBase Clone() { return new SelectManyWithIndexAsyncIterator(_source, _collectionSelector, _resultSelector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } _currentSource = default; await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _index = -1; _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } _currentSource = _sourceEnumerator.Current; checked { _index++; } var inner = _collectionSelector(_currentSource, _index); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = _resultSelector(_currentSource, _resultEnumerator.Current); return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } private sealed class SelectManyWithIndexAsyncIteratorWithTask : AsyncIterator { private const int State_Source = 1; private const int State_Result = 2; private readonly Func>> _collectionSelector; private readonly Func> _resultSelector; private readonly IAsyncEnumerable _source; private TSource _currentSource; private int _index; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyWithIndexAsyncIteratorWithTask(IAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { Debug.Assert(source != null); Debug.Assert(collectionSelector != null); Debug.Assert(resultSelector != null); _source = source; _collectionSelector = collectionSelector; _resultSelector = resultSelector; } public override AsyncIteratorBase Clone() { return new SelectManyWithIndexAsyncIteratorWithTask(_source, _collectionSelector, _resultSelector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } _currentSource = default; await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _index = -1; _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } _currentSource = _sourceEnumerator.Current; checked { _index++; } var inner = await _collectionSelector(_currentSource, _index).ConfigureAwait(false); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = await _resultSelector(_currentSource, _resultEnumerator.Current).ConfigureAwait(false); return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } #if !NO_DEEP_CANCELLATION private sealed class SelectManyWithIndexAsyncIteratorWithTaskAndCancellation : AsyncIterator { private const int State_Source = 1; private const int State_Result = 2; private readonly Func>> _collectionSelector; private readonly Func> _resultSelector; private readonly IAsyncEnumerable _source; private TSource _currentSource; private int _index; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyWithIndexAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable source, Func>> collectionSelector, Func> resultSelector) { Debug.Assert(source != null); Debug.Assert(collectionSelector != null); Debug.Assert(resultSelector != null); _source = source; _collectionSelector = collectionSelector; _resultSelector = resultSelector; } public override AsyncIteratorBase Clone() { return new SelectManyWithIndexAsyncIteratorWithTaskAndCancellation(_source, _collectionSelector, _resultSelector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } _currentSource = default; await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _index = -1; _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } _currentSource = _sourceEnumerator.Current; checked { _index++; } var inner = await _collectionSelector(_currentSource, _index, _cancellationToken).ConfigureAwait(false); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = await _resultSelector(_currentSource, _resultEnumerator.Current, _cancellationToken).ConfigureAwait(false); return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } #endif private sealed class SelectManyWithIndexAsyncIterator : AsyncIterator { private const int State_Source = 1; private const int State_Result = 2; private readonly Func> _selector; private readonly IAsyncEnumerable _source; private int _index; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyWithIndexAsyncIterator(IAsyncEnumerable source, Func> selector) { Debug.Assert(source != null); Debug.Assert(selector != null); _source = source; _selector = selector; } public override AsyncIteratorBase Clone() { return new SelectManyWithIndexAsyncIterator(_source, _selector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _index = -1; _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } checked { _index++; } var inner = _selector(_sourceEnumerator.Current, _index); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = _resultEnumerator.Current; return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } private sealed class SelectManyWithIndexAsyncIteratorWithTask : AsyncIterator { private const int State_Source = 1; private const int State_Result = 2; private readonly Func>> _selector; private readonly IAsyncEnumerable _source; private int _index; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyWithIndexAsyncIteratorWithTask(IAsyncEnumerable source, Func>> selector) { Debug.Assert(source != null); Debug.Assert(selector != null); _source = source; _selector = selector; } public override AsyncIteratorBase Clone() { return new SelectManyWithIndexAsyncIteratorWithTask(_source, _selector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _index = -1; _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } checked { _index++; } var inner = await _selector(_sourceEnumerator.Current, _index).ConfigureAwait(false); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = _resultEnumerator.Current; return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } #if !NO_DEEP_CANCELLATION private sealed class SelectManyWithIndexAsyncIteratorWithTaskAndCancellation : AsyncIterator { private const int State_Source = 1; private const int State_Result = 2; private readonly Func>> _selector; private readonly IAsyncEnumerable _source; private int _index; private int _mode; private IAsyncEnumerator _resultEnumerator; private IAsyncEnumerator _sourceEnumerator; public SelectManyWithIndexAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable source, Func>> selector) { Debug.Assert(source != null); Debug.Assert(selector != null); _source = source; _selector = selector; } public override AsyncIteratorBase Clone() { return new SelectManyWithIndexAsyncIteratorWithTaskAndCancellation(_source, _selector); } public override async ValueTask DisposeAsync() { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); _resultEnumerator = null; } if (_sourceEnumerator != null) { await _sourceEnumerator.DisposeAsync().ConfigureAwait(false); _sourceEnumerator = null; } await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken); _index = -1; _mode = State_Source; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: switch (_mode) { case State_Source: if (await _sourceEnumerator.MoveNextAsync().ConfigureAwait(false)) { if (_resultEnumerator != null) { await _resultEnumerator.DisposeAsync().ConfigureAwait(false); } checked { _index++; } var inner = await _selector(_sourceEnumerator.Current, _index, _cancellationToken).ConfigureAwait(false); _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken); _mode = State_Result; goto case State_Result; } break; case State_Result: if (await _resultEnumerator.MoveNextAsync().ConfigureAwait(false)) { _current = _resultEnumerator.Current; return true; } _mode = State_Source; goto case State_Source; // loop } break; } await DisposeAsync().ConfigureAwait(false); return false; } } #endif #endif } }