// 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; 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 Reverse(this IAsyncEnumerable source) { if (source == null) { throw new ArgumentNullException(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) { Debug.Assert(source != null); this.source = source; } public async Task ToArrayAsync(CancellationToken cancellationToken) { var array = await source.ToArray(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) { var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } public async Task> ToListAsync(CancellationToken cancellationToken) { var list = await source.ToList(cancellationToken).ConfigureAwait(false); list.Reverse(); return list; } public Task GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { if (source is IAsyncIListProvider listProv) { return listProv.GetCountAsync(true, cancellationToken); } if (!(source is ICollection) && !(source is ICollection)) { return Task.FromResult(-1); } } return source.Count(cancellationToken); } public override AsyncIterator 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(CancellationToken cancellationToken) { switch (state) { case AsyncIteratorState.Allocated: items = await source.ToArray().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; } } } }