using Cysharp.Threading.Tasks.Internal; using System; using System.Collections.Generic; using System.Threading; namespace Cysharp.Threading.Tasks.Linq { public static partial class UniTaskAsyncEnumerable { public static IUniTaskAsyncEnumerable SkipLast(this IUniTaskAsyncEnumerable source, Int32 count) { Error.ThrowArgumentNullException(source, nameof(source)); // non skip. if (count <= 0) { return source; } return new SkipLast(source, count); } } internal sealed class SkipLast : IUniTaskAsyncEnumerable { readonly IUniTaskAsyncEnumerable source; readonly int count; public SkipLast(IUniTaskAsyncEnumerable source, int count) { this.source = source; this.count = count; } public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { return new _SkipLast(source, count, cancellationToken); } sealed class _SkipLast : MoveNextSource, IUniTaskAsyncEnumerator { static readonly Action MoveNextCoreDelegate = MoveNextCore; readonly IUniTaskAsyncEnumerable source; readonly int count; CancellationToken cancellationToken; IUniTaskAsyncEnumerator enumerator; UniTask.Awaiter awaiter; Queue queue; bool continueNext; public _SkipLast(IUniTaskAsyncEnumerable source, int count, CancellationToken cancellationToken) { this.source = source; this.count = count; this.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(this, 3); } public TSource Current { get; private set; } public UniTask MoveNextAsync() { cancellationToken.ThrowIfCancellationRequested(); if (enumerator == null) { enumerator = source.GetAsyncEnumerator(cancellationToken); queue = new Queue(); } completionSource.Reset(); SourceMoveNext(); return new UniTask(this, completionSource.Version); } void SourceMoveNext() { try { LOOP: awaiter = enumerator.MoveNextAsync().GetAwaiter(); if (awaiter.IsCompleted) { continueNext = true; MoveNextCore(this); if (continueNext) { continueNext = false; goto LOOP; // avoid recursive } } else { awaiter.SourceOnCompleted(MoveNextCoreDelegate, this); } } catch (Exception ex) { completionSource.TrySetException(ex); } } static void MoveNextCore(object state) { var self = (_SkipLast)state; if (self.TryGetResult(self.awaiter, out var result)) { if (result) { if (self.queue.Count == self.count) { self.continueNext = false; var deq = self.queue.Dequeue(); self.Current = deq; self.queue.Enqueue(self.enumerator.Current); self.completionSource.TrySetResult(true); } else { self.queue.Enqueue(self.enumerator.Current); if (!self.continueNext) { self.SourceMoveNext(); } } } else { self.continueNext = false; self.completionSource.TrySetResult(false); } } else { self.continueNext = false; } } public UniTask DisposeAsync() { TaskTracker.RemoveTracking(this); if (enumerator != null) { return enumerator.DisposeAsync(); } return default; } } } }