// 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 { public static partial class AsyncEnumerable { #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.reverse?view=net-9.0-pp /// /// Inverts the order of the elements in a sequence. /// /// The type of the elements of source. /// An async-enumerable sequence of values to reverse. /// An async-enumerable sequence whose elements correspond to those of the input sequence in reverse order. /// is null. public static IAsyncEnumerable Reverse(this IAsyncEnumerable source) { if (source == null) throw Error.ArgumentNull(nameof(source)); return new ReverseAsyncIterator(source); } private sealed class ReverseAsyncIterator : AsyncIterator, IAsyncIListProvider { private readonly IAsyncEnumerable _source; private int _index; private TSource[]? _items; public ReverseAsyncIterator(IAsyncEnumerable source) { _source = source; } public async ValueTask ToArrayAsync(CancellationToken cancellationToken) { var array = await _source.ToArrayAsync(cancellationToken).ConfigureAwait(false); // Array.Reverse() involves boxing for non-primitive value types, but // checking that has its own cost, so just use this approach for all types. for (int i = 0, j = array.Length - 1; i < j; ++i, --j) { (array[j], array[i]) = (array[i], array[j]); } return array; } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var list = await _source.ToListAsync(cancellationToken).ConfigureAwait(false); list.Reverse(); return list; } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { if (_source is IAsyncIListProvider listProv) { return listProv.GetCountAsync(true, cancellationToken); } if (_source is not ICollection && _source is not ICollection) { return new ValueTask(-1); } } return _source.CountAsync(cancellationToken); } public override AsyncIteratorBase Clone() { return new ReverseAsyncIterator(_source); } public override async ValueTask DisposeAsync() { _items = null; // Just in case this ends up being long-lived, allow the memory to be reclaimed. await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _items = await _source.ToArrayAsync(_cancellationToken).ConfigureAwait(false); _index = _items.Length - 1; _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: if (_index != -1) { _current = _items![_index]; --_index; return true; } break; } await DisposeAsync().ConfigureAwait(false); return false; } } #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES } }