// 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; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace System.Linq { #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES // NB: Large portions of the implementation are based on https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/OrderedEnumerable.cs. internal abstract class OrderedAsyncEnumerable : AsyncIterator, IOrderedAsyncEnumerable, IAsyncPartition { protected readonly IAsyncEnumerable _source; private TElement[]? _buffer; private int[]? _indexes; private int _index; protected OrderedAsyncEnumerable(IAsyncEnumerable source) { _source = source ?? throw Error.ArgumentNull(nameof(source)); } 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); } IOrderedAsyncEnumerable IOrderedAsyncEnumerable.CreateOrderedEnumerable(Func> keySelector, IComparer? comparer, bool descending) { return new OrderedAsyncEnumerableWithTaskAndCancellation(_source, keySelector, comparer, descending, this); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _buffer = await _source.ToArrayAsync(_cancellationToken).ConfigureAwait(false); // TODO: Use buffer. var sorter = GetAsyncEnumerableSorter(_cancellationToken); _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, CancellationToken cancellationToken); internal AsyncEnumerableSorter GetAsyncEnumerableSorter(CancellationToken cancellationToken) => GetAsyncEnumerableSorter(next: null, cancellationToken); public async ValueTask ToArrayAsync(CancellationToken cancellationToken) { var elements = await AsyncEnumerableHelpers.ToArrayWithLength(_source, cancellationToken).ConfigureAwait(false); var count = elements.Length; if (count == 0) { return []; } var array = elements.Array; var map = await SortedMap(array, count, cancellationToken).ConfigureAwait(false); var result = new TElement[count]; for (var i = 0; i < result.Length; i++) { result[i] = array[map[i]]; } return result; } internal async ValueTask ToArrayAsync(int minIndexInclusive, int maxIndexInclusive, CancellationToken cancellationToken) { var elements = await AsyncEnumerableHelpers.ToArrayWithLength(_source, cancellationToken).ConfigureAwait(false); var count = elements.Length; if (count <= minIndexInclusive) { return []; } if (count <= maxIndexInclusive) { maxIndexInclusive = count - 1; } var array = elements.Array; if (minIndexInclusive == maxIndexInclusive) { var sorter = GetAsyncEnumerableSorter(cancellationToken); var element = await sorter.ElementAt(array, count, minIndexInclusive).ConfigureAwait(false); return [element]; } var map = await SortedMap(array, count, minIndexInclusive, maxIndexInclusive, cancellationToken).ConfigureAwait(false); var result = new TElement[maxIndexInclusive - minIndexInclusive + 1]; for (var i = 0; minIndexInclusive <= maxIndexInclusive; i++) { result[i] = array[map[minIndexInclusive++]]; } return result; } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var elements = await AsyncEnumerableHelpers.ToArrayWithLength(_source, cancellationToken).ConfigureAwait(false); var count = elements.Length; if (count == 0) { return []; } var array = elements.Array; var map = await SortedMap(array, count, cancellationToken).ConfigureAwait(false); var result = new List(count); for (var i = 0; i < count; i++) { result.Add(array[map[i]]); } return result; } internal async ValueTask> ToListAsync(int minIndexInclusive, int maxIndexInclusive, CancellationToken cancellationToken) { var elements = await AsyncEnumerableHelpers.ToArrayWithLength(_source, cancellationToken).ConfigureAwait(false); var count = elements.Length; if (count <= minIndexInclusive) { return []; } if (count <= maxIndexInclusive) { maxIndexInclusive = count - 1; } var array = elements.Array; if (minIndexInclusive == maxIndexInclusive) { var sorter = GetAsyncEnumerableSorter(cancellationToken); var element = await sorter.ElementAt(array, count, minIndexInclusive).ConfigureAwait(false); return [element]; } var map = await SortedMap(array, count, minIndexInclusive, maxIndexInclusive, cancellationToken).ConfigureAwait(false); var list = new List(maxIndexInclusive - minIndexInclusive + 1); while (minIndexInclusive <= maxIndexInclusive) { list.Add(array[map[minIndexInclusive++]]); } return list; } public async ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (_source is IAsyncIListProvider listProv) { var count = await listProv.GetCountAsync(onlyIfCheap, cancellationToken).ConfigureAwait(false); return count; } return !onlyIfCheap || _source is ICollection || _source is ICollection ? await _source.CountAsync(cancellationToken).ConfigureAwait(false) : -1; } internal async ValueTask GetCountAsync(int minIndexInclusive, int maxIndexInclusive, bool onlyIfCheap, CancellationToken cancellationToken) { var count = await GetCountAsync(onlyIfCheap, cancellationToken).ConfigureAwait(false); if (count <= 0) { return count; } if (count <= minIndexInclusive) { return 0; } return (count <= maxIndexInclusive ? count : maxIndexInclusive + 1) - minIndexInclusive; } private ValueTask SortedMap(TElement[] elements, int count, CancellationToken cancellationToken) { var sorter = GetAsyncEnumerableSorter(cancellationToken); return sorter.Sort(elements, count); } private ValueTask SortedMap(TElement[] elements, int count, int minIndexInclusive, int maxIndexInclusive, CancellationToken cancellationToken) { var sorter = GetAsyncEnumerableSorter(cancellationToken); return sorter.Sort(elements, count, minIndexInclusive, maxIndexInclusive); } private AsyncCachingComparer GetComparer() => GetComparer(childComparer: null); internal abstract AsyncCachingComparer GetComparer(AsyncCachingComparer? childComparer); public async ValueTask> TryGetFirstAsync(CancellationToken cancellationToken) { await using var e = _source.GetConfiguredAsyncEnumerator(cancellationToken, false); if (!await e.MoveNextAsync()) { return new Maybe(); } var value = e.Current; var comparer = GetComparer(); await comparer.SetElement(value, cancellationToken).ConfigureAwait(false); while (await e.MoveNextAsync()) { var x = e.Current; if (await comparer.Compare(x, cacheLower: true, cancellationToken).ConfigureAwait(false) < 0) { value = x; } } return new Maybe(value); } public async ValueTask> TryGetLastAsync(CancellationToken cancellationToken) { await using var e = _source.GetConfiguredAsyncEnumerator(cancellationToken, false); if (!await e.MoveNextAsync()) { return new Maybe(); } var value = e.Current; var comparer = GetComparer(); await comparer.SetElement(value, cancellationToken).ConfigureAwait(false); while (await e.MoveNextAsync()) { var current = e.Current; if (await comparer.Compare(current, cacheLower: false, cancellationToken).ConfigureAwait(false) >= 0) { value = current; } } return new Maybe(value); } internal async ValueTask> TryGetLastAsync(int minIndexInclusive, int maxIndexInclusive, CancellationToken cancellationToken) { var elements = await AsyncEnumerableHelpers.ToArrayWithLength(_source, cancellationToken).ConfigureAwait(false); var count = elements.Length; if (minIndexInclusive >= count) { return new Maybe(); } var array = elements.Array; TElement last; if (maxIndexInclusive < count - 1) { var sorter = GetAsyncEnumerableSorter(cancellationToken); last = await sorter.ElementAt(array, count, maxIndexInclusive).ConfigureAwait(false); } else { last = await Last(array, count, cancellationToken).ConfigureAwait(false); } return new Maybe(last); } private async ValueTask Last(TElement[] items, int count, CancellationToken cancellationToken) { var value = items[0]; var comparer = GetComparer(); await comparer.SetElement(value, cancellationToken).ConfigureAwait(false); for (var i = 1; i != count; ++i) { var x = items[i]; if (await comparer.Compare(x, cacheLower: false, cancellationToken).ConfigureAwait(false) >= 0) { value = x; } } return value; } public IAsyncPartition Skip(int count) => new OrderedAsyncPartition(this, count, int.MaxValue); public IAsyncPartition Take(int count) => new OrderedAsyncPartition(this, 0, count - 1); public ValueTask> TryGetElementAtAsync(int index, CancellationToken cancellationToken) { if (index == 0) { return TryGetFirstAsync(cancellationToken); } if (index > 0) { return Core(); async ValueTask> Core() { var elements = await AsyncEnumerableHelpers.ToArrayWithLength(_source, cancellationToken).ConfigureAwait(false); var count = elements.Length; if (index < count) { var sorter = GetAsyncEnumerableSorter(cancellationToken); var element = await sorter.ElementAt(elements.Array, count, index).ConfigureAwait(false); return new Maybe(element); } return new Maybe(); } } return new ValueTask>(new Maybe()); } } 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) : base(source) { _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _comparer = comparer ?? Comparer.Default; _descending = descending; _parent = parent; } public override AsyncIteratorBase Clone() { return new OrderedAsyncEnumerable(_source, _keySelector, _comparer, _descending, _parent); } internal override AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter? next, CancellationToken cancellationToken) { var sorter = new SyncKeySelectorAsyncEnumerableSorter(_keySelector, _comparer, _descending, next); if (_parent != null) { return _parent.GetAsyncEnumerableSorter(sorter, cancellationToken); } return sorter; } internal override AsyncCachingComparer GetComparer(AsyncCachingComparer? childComparer) { AsyncCachingComparer cmp = childComparer == null ? new AsyncCachingComparer(_keySelector, _comparer, _descending) : new AsyncCachingComparerWithChild(_keySelector, _comparer, _descending, childComparer); return _parent != null ? _parent.GetComparer(cmp) : cmp; } } 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) : base(source) { _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _comparer = comparer ?? Comparer.Default; _descending = descending; _parent = parent; } public override AsyncIteratorBase Clone() { return new OrderedAsyncEnumerableWithTask(_source, _keySelector, _comparer, _descending, _parent); } internal override AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter? next, CancellationToken cancellationToken) { var sorter = new AsyncKeySelectorAsyncEnumerableSorter(_keySelector, _comparer, _descending, next); if (_parent != null) { return _parent.GetAsyncEnumerableSorter(sorter, cancellationToken); } return sorter; } internal override AsyncCachingComparer GetComparer(AsyncCachingComparer? childComparer) { AsyncCachingComparer cmp = childComparer == null ? new AsyncCachingComparerWithTask(_keySelector, _comparer, _descending) : new AsyncCachingComparerWithTaskAndChild(_keySelector, _comparer, _descending, childComparer); return _parent != null ? _parent.GetComparer(cmp) : cmp; } } #if !NO_DEEP_CANCELLATION internal sealed class OrderedAsyncEnumerableWithTaskAndCancellation : OrderedAsyncEnumerable { private readonly IComparer _comparer; private readonly bool _descending; private readonly Func> _keySelector; private readonly OrderedAsyncEnumerable? _parent; public OrderedAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable source, Func> keySelector, IComparer? comparer, bool descending, OrderedAsyncEnumerable? parent) : base(source) { _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _comparer = comparer ?? Comparer.Default; _descending = descending; _parent = parent; } public override AsyncIteratorBase Clone() { return new OrderedAsyncEnumerableWithTaskAndCancellation(_source, _keySelector, _comparer, _descending, _parent); } internal override AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter? next, CancellationToken cancellationToken) { var sorter = new AsyncKeySelectorAsyncEnumerableSorterWithCancellation(_keySelector, _comparer, _descending, next, cancellationToken); if (_parent != null) { return _parent.GetAsyncEnumerableSorter(sorter, cancellationToken); } return sorter; } internal override AsyncCachingComparer GetComparer(AsyncCachingComparer? childComparer) { AsyncCachingComparer cmp = childComparer == null ? new AsyncCachingComparerWithTaskAndCancellation(_keySelector, _comparer, _descending) : new AsyncCachingComparerWithTaskAndCancellationAndChild(_keySelector, _comparer, _descending, childComparer); return _parent != null ? _parent.GetComparer(cmp) : cmp; } } #endif internal abstract class AsyncEnumerableSorter { internal abstract ValueTask ComputeKeys(TElement[] elements, int count); internal abstract int CompareAnyKeys(int index1, int index2); public async ValueTask Sort(TElement[] elements, int count) { var map = await ComputeMap(elements, count).ConfigureAwait(false); QuickSort(map, 0, count - 1); return map; } public async ValueTask Sort(TElement[] elements, int count, int minIndexInclusive, int maxIndexInclusive) { var map = await ComputeMap(elements, count).ConfigureAwait(false); PartialQuickSort(map, 0, count - 1, minIndexInclusive, maxIndexInclusive); return map; } public async ValueTask ElementAt(TElement[] elements, int count, int index) { var map = await ComputeMap(elements, count).ConfigureAwait(false); return index == 0 ? elements[Min(map, count)] : elements[QuickSelect(map, count - 1, index)]; } private async ValueTask ComputeMap(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; } return map; } protected abstract void QuickSort(int[] map, int left, int right); protected abstract void PartialQuickSort(int[] map, int left, int right, int minIndexInclusive, int maxIndexInclusive); protected abstract int QuickSelect(int[] map, int right, int idx); protected abstract int Min(int[] map, int count); } 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; _descending = descending; _next = next; } internal sealed override int CompareAnyKeys(int index1, int index2) { var c = _comparer.Compare(_keys![index1], _keys[index2]); // NB: _keys is assigned before calling this method. if (c == 0) { return _next == null ? index1 - index2 : _next.CompareAnyKeys(index1, index2); } else { return (_descending != (c > 0)) ? 1 : -1; } } private int CompareKeys(int index1, int index2) => index1 == index2 ? 0 : CompareAnyKeys(index1, index2); protected override void QuickSort(int[] keys, int lo, int hi) => Array.Sort(keys, lo, hi - lo + 1, Comparer.Create(CompareAnyKeys)); protected override void PartialQuickSort(int[] map, int left, int right, int minIndexInclusive, int maxIndexInclusive) { 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) { (map[j], map[i]) = (map[i], map[j]); } i++; j--; } while (i <= j); if (minIndexInclusive >= i) { left = i + 1; } else if (maxIndexInclusive <= j) { right = j - 1; } if (j - left <= right - i) { if (left < j) { PartialQuickSort(map, left, j, minIndexInclusive, maxIndexInclusive); } left = i; } else { if (i < right) { PartialQuickSort(map, i, right, minIndexInclusive, maxIndexInclusive); } right = j; } } while (left < right); } protected override int QuickSelect(int[] map, int right, int idx) { var left = 0; 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) { (map[j], map[i]) = (map[i], map[j]); } i++; j--; } while (i <= j); if (i <= idx) { left = i + 1; } else { right = j - 1; } if (j - left <= right - i) { if (left < j) { right = j; } left = i; } else { if (i < right) { left = i; } right = j; } } while (left < right); return map[idx]; } protected override int Min(int[] map, int count) { var index = 0; for (var i = 1; i < count; i++) { if (CompareKeys(map[i], map[index]) < 0) { index = i; } } return map[index]; } } 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); } } } #if !NO_DEEP_CANCELLATION internal sealed class AsyncKeySelectorAsyncEnumerableSorterWithCancellation : AsyncEnumerableSorterBase { private readonly Func> _keySelector; private readonly CancellationToken _cancellationToken; public AsyncKeySelectorAsyncEnumerableSorterWithCancellation(Func> keySelector, IComparer comparer, bool descending, AsyncEnumerableSorter? next, CancellationToken cancellationToken) : base(comparer, descending, next) { _keySelector = keySelector; _cancellationToken = cancellationToken; } 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], _cancellationToken).ConfigureAwait(false); } if (_next != null) { await _next.ComputeKeys(elements, count).ConfigureAwait(false); } } } #endif internal sealed class OrderedAsyncPartition : AsyncIterator, IAsyncPartition { private readonly OrderedAsyncEnumerable _source; private readonly int _minIndexInclusive; private readonly int _maxIndexInclusive; public OrderedAsyncPartition(OrderedAsyncEnumerable source, int minIndexInclusive, int maxIndexInclusive) { _source = source; _minIndexInclusive = minIndexInclusive; _maxIndexInclusive = maxIndexInclusive; } public override AsyncIteratorBase Clone() => new OrderedAsyncPartition(_source, _minIndexInclusive, _maxIndexInclusive); public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) => _source.GetCountAsync(_minIndexInclusive, _maxIndexInclusive, onlyIfCheap, cancellationToken); public IAsyncPartition Skip(int count) { var minIndex = unchecked(_minIndexInclusive + count); if (unchecked((uint)minIndex > (uint)_maxIndexInclusive)) { return AsyncEnumerable.EmptyAsyncIterator.Instance; } return new OrderedAsyncPartition(_source, minIndex, _maxIndexInclusive); } public IAsyncPartition Take(int count) { var maxIndex = unchecked(_minIndexInclusive + count - 1); if (unchecked((uint)maxIndex >= (uint)_maxIndexInclusive)) { return this; } return new OrderedAsyncPartition(_source, _minIndexInclusive, maxIndex); } public ValueTask ToArrayAsync(CancellationToken cancellationToken) => _source.ToArrayAsync(_minIndexInclusive, _maxIndexInclusive, cancellationToken); public ValueTask> ToListAsync(CancellationToken cancellationToken) => _source.ToListAsync(_minIndexInclusive, _maxIndexInclusive, cancellationToken); public ValueTask> TryGetElementAtAsync(int index, CancellationToken cancellationToken) { if (unchecked((uint)index <= (uint)(_maxIndexInclusive - _minIndexInclusive))) { return _source.TryGetElementAtAsync(index + _minIndexInclusive, cancellationToken); } return new ValueTask>(new Maybe()); } public ValueTask> TryGetFirstAsync(CancellationToken cancellationToken) => _source.TryGetElementAtAsync(_minIndexInclusive, cancellationToken); public ValueTask> TryGetLastAsync(CancellationToken cancellationToken) => _source.TryGetLastAsync(_minIndexInclusive, _maxIndexInclusive, cancellationToken); // REVIEW: Consider to tear off an iterator object rather than storing this state here? private TElement[]? _buffer; private int[]? _indexes; private int _minIndexIterator; private int _maxIndexIterator; protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _buffer = await _source.ToArrayAsync(_cancellationToken).ConfigureAwait(false); // TODO: Use buffer. _minIndexIterator = _minIndexInclusive; _maxIndexIterator = _maxIndexInclusive; var count = _buffer.Length; if (count > _minIndexIterator) { if (count <= _maxIndexIterator) { _maxIndexIterator = count - 1; } var sorter = _source.GetAsyncEnumerableSorter(_cancellationToken); if (_minIndexIterator == _maxIndexIterator) { _current = await sorter.ElementAt(_buffer, _buffer.Length, _minIndexIterator).ConfigureAwait(false); _minIndexIterator = int.MaxValue; _maxIndexIterator = int.MinValue; _state = AsyncIteratorState.Iterating; return true; } else { _indexes = await sorter.Sort(_buffer, _buffer.Length, _minIndexIterator, _maxIndexIterator).ConfigureAwait(false); } _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; } await DisposeAsync(); break; case AsyncIteratorState.Iterating: if (_minIndexIterator <= _maxIndexIterator) { _current = _buffer![_indexes![_minIndexIterator++]]; 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 class AsyncCachingComparer { internal abstract ValueTask Compare(TElement element, bool cacheLower, CancellationToken cancellationToken); internal abstract ValueTask SetElement(TElement element, CancellationToken cancellationToken); } internal class AsyncCachingComparer : AsyncCachingComparer { protected readonly Func _keySelector; protected readonly IComparer _comparer; protected readonly bool _descending; protected TKey _lastKey; public AsyncCachingComparer(Func keySelector, IComparer comparer, bool descending) { _keySelector = keySelector; _comparer = comparer; _descending = descending; _lastKey = default!; } internal override ValueTask Compare(TElement element, bool cacheLower, CancellationToken cancellationToken) { var newKey = _keySelector(element); var cmp = _descending ? _comparer.Compare(_lastKey, newKey) : _comparer.Compare(newKey, _lastKey); if (cacheLower == cmp < 0) { _lastKey = newKey; } return new ValueTask(cmp); } internal override ValueTask SetElement(TElement element, CancellationToken cancellationToken) { _lastKey = _keySelector(element); return new ValueTask(); } } internal sealed class AsyncCachingComparerWithChild : AsyncCachingComparer { private readonly AsyncCachingComparer _child; public AsyncCachingComparerWithChild(Func keySelector, IComparer comparer, bool descending, AsyncCachingComparer child) : base(keySelector, comparer, descending) { _child = child; } internal override async ValueTask Compare(TElement element, bool cacheLower, CancellationToken cancellationToken) { var newKey = _keySelector(element); var cmp = _descending ? _comparer.Compare(_lastKey, newKey) : _comparer.Compare(newKey, _lastKey); if (cmp == 0) { return await _child.Compare(element, cacheLower, cancellationToken).ConfigureAwait(false); } if (cacheLower == cmp < 0) { _lastKey = newKey; await _child.SetElement(element, cancellationToken).ConfigureAwait(false); } return cmp; } internal override async ValueTask SetElement(TElement element, CancellationToken cancellationToken) { await base.SetElement(element, cancellationToken).ConfigureAwait(false); await _child.SetElement(element, cancellationToken).ConfigureAwait(false); } } internal class AsyncCachingComparerWithTask : AsyncCachingComparer { protected readonly Func> _keySelector; protected readonly IComparer _comparer; protected readonly bool _descending; protected TKey _lastKey; public AsyncCachingComparerWithTask(Func> keySelector, IComparer comparer, bool descending) { _keySelector = keySelector; _comparer = comparer; _descending = descending; _lastKey = default!; } internal override async ValueTask Compare(TElement element, bool cacheLower, CancellationToken cancellationToken) { var newKey = await _keySelector(element).ConfigureAwait(false); var cmp = _descending ? _comparer.Compare(_lastKey, newKey) : _comparer.Compare(newKey, _lastKey); if (cacheLower == cmp < 0) { _lastKey = newKey; } return cmp; } internal override async ValueTask SetElement(TElement element, CancellationToken cancellationToken) { _lastKey = await _keySelector(element).ConfigureAwait(false); } } internal sealed class AsyncCachingComparerWithTaskAndChild : AsyncCachingComparerWithTask { private readonly AsyncCachingComparer _child; public AsyncCachingComparerWithTaskAndChild(Func> keySelector, IComparer comparer, bool descending, AsyncCachingComparer child) : base(keySelector, comparer, descending) { _child = child; } internal override async ValueTask Compare(TElement element, bool cacheLower, CancellationToken cancellationToken) { var newKey = await _keySelector(element).ConfigureAwait(false); var cmp = _descending ? _comparer.Compare(_lastKey, newKey) : _comparer.Compare(newKey, _lastKey); if (cmp == 0) { return await _child.Compare(element, cacheLower, cancellationToken).ConfigureAwait(false); } if (cacheLower == cmp < 0) { _lastKey = newKey; await _child.SetElement(element, cancellationToken).ConfigureAwait(false); } return cmp; } internal override async ValueTask SetElement(TElement element, CancellationToken cancellationToken) { await base.SetElement(element, cancellationToken).ConfigureAwait(false); await _child.SetElement(element, cancellationToken).ConfigureAwait(false); } } #if !NO_DEEP_CANCELLATION internal class AsyncCachingComparerWithTaskAndCancellation : AsyncCachingComparer { protected readonly Func> _keySelector; protected readonly IComparer _comparer; protected readonly bool _descending; protected TKey _lastKey; public AsyncCachingComparerWithTaskAndCancellation(Func> keySelector, IComparer comparer, bool descending) { _keySelector = keySelector; _comparer = comparer; _descending = descending; _lastKey = default!; } internal override async ValueTask Compare(TElement element, bool cacheLower, CancellationToken cancellationToken) { var newKey = await _keySelector(element, cancellationToken).ConfigureAwait(false); var cmp = _descending ? _comparer.Compare(_lastKey, newKey) : _comparer.Compare(newKey, _lastKey); if (cacheLower == cmp < 0) { _lastKey = newKey; } return cmp; } internal override async ValueTask SetElement(TElement element, CancellationToken cancellationToken) { _lastKey = await _keySelector(element, cancellationToken).ConfigureAwait(false); } } internal sealed class AsyncCachingComparerWithTaskAndCancellationAndChild : AsyncCachingComparerWithTaskAndCancellation { private readonly AsyncCachingComparer _child; public AsyncCachingComparerWithTaskAndCancellationAndChild(Func> keySelector, IComparer comparer, bool descending, AsyncCachingComparer child) : base(keySelector, comparer, descending) { _child = child; } internal override async ValueTask Compare(TElement element, bool cacheLower, CancellationToken cancellationToken) { var newKey = await _keySelector(element, cancellationToken).ConfigureAwait(false); var cmp = _descending ? _comparer.Compare(_lastKey, newKey) : _comparer.Compare(newKey, _lastKey); if (cmp == 0) { return await _child.Compare(element, cacheLower, cancellationToken).ConfigureAwait(false); } if (cacheLower == cmp < 0) { _lastKey = newKey; await _child.SetElement(element, cancellationToken).ConfigureAwait(false); } return cmp; } internal override async ValueTask SetElement(TElement element, CancellationToken cancellationToken) { await base.SetElement(element, cancellationToken).ConfigureAwait(false); await _child.SetElement(element, cancellationToken).ConfigureAwait(false); } } #endif #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES }