// 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 { // TODO: Add optimizations for First, Last, and ElementAt. internal abstract class OrderedAsyncEnumerable : AsyncIterator, IOrderedAsyncEnumerable { protected IAsyncEnumerable _source; private TElement[] _buffer; private int[] _indexes; private int _index; IOrderedAsyncEnumerable IOrderedAsyncEnumerable.CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending) { return new OrderedAsyncEnumerable(_source, keySelector, comparer, descending, this); } IOrderedAsyncEnumerable IOrderedAsyncEnumerable.CreateOrderedEnumerable(Func> keySelector, IComparer comparer, bool descending) { return new OrderedAsyncEnumerableWithTask(_source, keySelector, comparer, descending, this); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _buffer = await _source.ToArray(_cancellationToken).ConfigureAwait(false); // REVIEW: If we add selectors with CancellationToken support, we should feed the token to Sort. var sorter = GetAsyncEnumerableSorter(next: null); _indexes = await sorter.Sort(_buffer, _buffer.Length).ConfigureAwait(false); _index = 0; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: if (_index < _buffer.Length) { _current = _buffer[_indexes[_index++]]; return true; } await DisposeAsync().ConfigureAwait(false); break; } return false; } public override async ValueTask DisposeAsync() { _buffer = null; _indexes = null; await base.DisposeAsync().ConfigureAwait(false); } internal abstract AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter next); } internal sealed class OrderedAsyncEnumerable : OrderedAsyncEnumerable { private readonly IComparer _comparer; private readonly bool _descending; private readonly Func _keySelector; private readonly OrderedAsyncEnumerable _parent; public OrderedAsyncEnumerable(IAsyncEnumerable source, Func keySelector, IComparer comparer, bool descending, OrderedAsyncEnumerable parent) { Debug.Assert(source != null); Debug.Assert(keySelector != null); _source = source; _keySelector = keySelector; _comparer = comparer; _descending = descending; _parent = parent; } public override AsyncIterator Clone() { return new OrderedAsyncEnumerable(_source, _keySelector, _comparer, _descending, _parent); } internal override AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter next) { var sorter = new SyncKeySelectorAsyncEnumerableSorter(_keySelector, _comparer, _descending, next); if (_parent != null) { return _parent.GetAsyncEnumerableSorter(sorter); } return sorter; } } internal sealed class OrderedAsyncEnumerableWithTask : OrderedAsyncEnumerable { private readonly IComparer _comparer; private readonly bool _descending; private readonly Func> _keySelector; private readonly OrderedAsyncEnumerable _parent; public OrderedAsyncEnumerableWithTask(IAsyncEnumerable source, Func> keySelector, IComparer comparer, bool descending, OrderedAsyncEnumerable parent) { Debug.Assert(source != null); Debug.Assert(keySelector != null); _source = source; _keySelector = keySelector; _comparer = comparer; _descending = descending; _parent = parent; } public override AsyncIterator Clone() { return new OrderedAsyncEnumerableWithTask(_source, _keySelector, _comparer, _descending, _parent); } internal override AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter next) { var sorter = new AsyncKeySelectorAsyncEnumerableSorter(_keySelector, _comparer, _descending, next); if (_parent != null) { return _parent.GetAsyncEnumerableSorter(sorter); } return sorter; } } internal abstract class AsyncEnumerableSorter { internal abstract ValueTask ComputeKeys(TElement[] elements, int count); internal abstract int CompareKeys(int index1, int index2); public async ValueTask Sort(TElement[] elements, int count) { await ComputeKeys(elements, count).ConfigureAwait(false); var map = new int[count]; for (var i = 0; i < count; i++) { map[i] = i; } QuickSort(map, 0, count - 1); return map; } private void QuickSort(int[] map, int left, int right) { do { var i = left; var j = right; var x = map[i + (j - i >> 1)]; do { while (i < map.Length && CompareKeys(x, map[i]) > 0) { i++; } while (j >= 0 && CompareKeys(x, map[j]) < 0) { j--; } if (i > j) { break; } if (i < j) { var temp = map[i]; map[i] = map[j]; map[j] = temp; } i++; j--; } while (i <= j); if (j - left <= right - i) { if (left < j) { QuickSort(map, left, j); } left = i; } else { if (i < right) { QuickSort(map, i, right); } right = j; } } while (left < right); } } internal abstract class AsyncEnumerableSorterBase : AsyncEnumerableSorter { private readonly IComparer _comparer; private readonly bool _descending; protected readonly AsyncEnumerableSorter _next; protected TKey[] _keys; public AsyncEnumerableSorterBase(IComparer comparer, bool descending, AsyncEnumerableSorter next) { _comparer = comparer ?? Comparer.Default; _descending = descending; _next = next; } internal override int CompareKeys(int index1, int index2) { var c = _comparer.Compare(_keys[index1], _keys[index2]); if (c == 0) { return _next == null ? index1 - index2 : _next.CompareKeys(index1, index2); } else { return (_descending != (c > 0)) ? 1 : -1; } } } internal sealed class SyncKeySelectorAsyncEnumerableSorter : AsyncEnumerableSorterBase { private readonly Func _keySelector; public SyncKeySelectorAsyncEnumerableSorter(Func keySelector, IComparer comparer, bool descending, AsyncEnumerableSorter next) : base(comparer, descending, next) { _keySelector = keySelector; } internal override async ValueTask ComputeKeys(TElement[] elements, int count) { _keys = new TKey[count]; for (var i = 0; i < count; i++) { _keys[i] = _keySelector(elements[i]); } if (_next != null) { await _next.ComputeKeys(elements, count).ConfigureAwait(false); } } } internal sealed class AsyncKeySelectorAsyncEnumerableSorter : AsyncEnumerableSorterBase { private readonly Func> _keySelector; public AsyncKeySelectorAsyncEnumerableSorter(Func> keySelector, IComparer comparer, bool descending, AsyncEnumerableSorter next) : base(comparer, descending, next) { _keySelector = keySelector; } internal override async ValueTask ComputeKeys(TElement[] elements, int count) { _keys = new TKey[count]; for (var i = 0; i < count; i++) { _keys[i] = await _keySelector(elements[i]).ConfigureAwait(false); } if (_next != null) { await _next.ComputeKeys(elements, count).ConfigureAwait(false); } } } }