// 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
}
}