From 716decd199bbf6d1e7bb04f4a63688bcab9c8422 Mon Sep 17 00:00:00 2001 From: neuecc Date: Sun, 10 May 2020 02:39:13 +0900 Subject: [PATCH] ToDict, ToLookup, ToList, TOHashSet, ToObservable --- src/UniTask.NetCore/IAsyncEnumerable.cs | 1 - src/UniTask.NetCore/Linq/AsEnumerable.cs | 775 ---------- .../Linq/AsUniTaskAsyncEnumerable.cs | 10 + src/UniTask.NetCore/Linq/Empty.cs | 1 - src/UniTask.NetCore/Linq/Never.cs | 1 - src/UniTask.NetCore/Linq/Range.cs | 1 - src/UniTask.NetCore/Linq/Repeat.cs | 1 - src/UniTask.NetCore/Linq/Return.cs | 1 - src/UniTask.NetCore/Linq/Select.cs | 6 - src/UniTask.NetCore/Linq/ToArray.cs | 11 +- src/UniTask.NetCore/Linq/ToDictionary.cs | 1045 ++++--------- src/UniTask.NetCore/Linq/ToEnumerable.cs | 775 ---------- src/UniTask.NetCore/Linq/ToHashSet.cs | 809 +--------- src/UniTask.NetCore/Linq/ToList.cs | 809 +--------- src/UniTask.NetCore/Linq/ToLookup.cs | 1311 +++++++---------- src/UniTask.NetCore/Linq/ToObservable.cs | 864 ++--------- .../Linq/ToUniTaskAsyncEnumerable.cs | 4 - src/UniTask.NetCore/Linq/_FileMaker.cs | 163 +- src/UniTask.NetCoreSandbox/Program.cs | 19 +- src/UniTask.NetCoreTests/Linq/Convert.cs | 117 ++ 20 files changed, 1133 insertions(+), 5591 deletions(-) delete mode 100644 src/UniTask.NetCore/Linq/AsEnumerable.cs create mode 100644 src/UniTask.NetCore/Linq/AsUniTaskAsyncEnumerable.cs delete mode 100644 src/UniTask.NetCore/Linq/ToEnumerable.cs diff --git a/src/UniTask.NetCore/IAsyncEnumerable.cs b/src/UniTask.NetCore/IAsyncEnumerable.cs index dfc88e1..34d0218 100644 --- a/src/UniTask.NetCore/IAsyncEnumerable.cs +++ b/src/UniTask.NetCore/IAsyncEnumerable.cs @@ -41,7 +41,6 @@ namespace Cysharp.Threading.Tasks public Enumerator GetAsyncEnumerator() { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(enumerable.GetAsyncEnumerator(cancellationToken)); } diff --git a/src/UniTask.NetCore/Linq/AsEnumerable.cs b/src/UniTask.NetCore/Linq/AsEnumerable.cs deleted file mode 100644 index 4877fa5..0000000 --- a/src/UniTask.NetCore/Linq/AsEnumerable.cs +++ /dev/null @@ -1,775 +0,0 @@ -namespace Cysharp.Threading.Tasks.Linq -{ - internal sealed class AsEnumerable - { - } - - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniTask.NetCore/Linq/AsUniTaskAsyncEnumerable.cs b/src/UniTask.NetCore/Linq/AsUniTaskAsyncEnumerable.cs new file mode 100644 index 0000000..c00452e --- /dev/null +++ b/src/UniTask.NetCore/Linq/AsUniTaskAsyncEnumerable.cs @@ -0,0 +1,10 @@ +namespace Cysharp.Threading.Tasks.Linq +{ + public static partial class UniTaskAsyncEnumerable + { + public static IUniTaskAsyncEnumerable AsUniTaskAsyncEnumerable(this IUniTaskAsyncEnumerable source) + { + return source; + } + } +} diff --git a/src/UniTask.NetCore/Linq/Empty.cs b/src/UniTask.NetCore/Linq/Empty.cs index 8d2f288..2b4adea 100644 --- a/src/UniTask.NetCore/Linq/Empty.cs +++ b/src/UniTask.NetCore/Linq/Empty.cs @@ -20,7 +20,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return Enumerator.Instance; } diff --git a/src/UniTask.NetCore/Linq/Never.cs b/src/UniTask.NetCore/Linq/Never.cs index 88a1e00..a735812 100644 --- a/src/UniTask.NetCore/Linq/Never.cs +++ b/src/UniTask.NetCore/Linq/Never.cs @@ -20,7 +20,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(cancellationToken); } diff --git a/src/UniTask.NetCore/Linq/Range.cs b/src/UniTask.NetCore/Linq/Range.cs index 35a4713..09c1f17 100644 --- a/src/UniTask.NetCore/Linq/Range.cs +++ b/src/UniTask.NetCore/Linq/Range.cs @@ -31,7 +31,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(start, end, cancellationToken); } diff --git a/src/UniTask.NetCore/Linq/Repeat.cs b/src/UniTask.NetCore/Linq/Repeat.cs index 405a96f..e74ddc2 100644 --- a/src/UniTask.NetCore/Linq/Repeat.cs +++ b/src/UniTask.NetCore/Linq/Repeat.cs @@ -26,7 +26,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(element, count, cancellationToken); } diff --git a/src/UniTask.NetCore/Linq/Return.cs b/src/UniTask.NetCore/Linq/Return.cs index e7308f7..b5364d4 100644 --- a/src/UniTask.NetCore/Linq/Return.cs +++ b/src/UniTask.NetCore/Linq/Return.cs @@ -22,7 +22,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(value, cancellationToken); } diff --git a/src/UniTask.NetCore/Linq/Select.cs b/src/UniTask.NetCore/Linq/Select.cs index eff3144..ca1479f 100644 --- a/src/UniTask.NetCore/Linq/Select.cs +++ b/src/UniTask.NetCore/Linq/Select.cs @@ -68,7 +68,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, selector, cancellationToken); } @@ -112,7 +111,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, selector, cancellationToken); } @@ -157,7 +155,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, selector, cancellationToken); } @@ -197,7 +194,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, selector, cancellationToken); } @@ -238,7 +234,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, selector, cancellationToken); } @@ -278,7 +273,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, selector, cancellationToken); } diff --git a/src/UniTask.NetCore/Linq/ToArray.cs b/src/UniTask.NetCore/Linq/ToArray.cs index 0410232..25c56ec 100644 --- a/src/UniTask.NetCore/Linq/ToArray.cs +++ b/src/UniTask.NetCore/Linq/ToArray.cs @@ -9,21 +9,24 @@ namespace Cysharp.Threading.Tasks.Linq { public static UniTask ToArrayAsync(this IUniTaskAsyncEnumerable source, CancellationToken cancellationToken = default) { - return Cysharp.Threading.Tasks.Linq.ToArray.InvokeAsync(source, cancellationToken); + Error.ThrowArgumentNullException(source, nameof(source)); + + return Cysharp.Threading.Tasks.Linq.ToArray.InvokeAsync(source, cancellationToken); } } - internal static class ToArray + internal static class ToArray { - internal static async UniTask InvokeAsync(IUniTaskAsyncEnumerable source, CancellationToken cancellationToken) + internal static async UniTask InvokeAsync(IUniTaskAsyncEnumerable source, CancellationToken cancellationToken) { var pool = ArrayPool.Shared; var array = pool.Rent(16); TSource[] result = default; - var e = source.GetAsyncEnumerator(cancellationToken); + IUniTaskAsyncEnumerator e = default; try { + e = source.GetAsyncEnumerator(cancellationToken); var i = 0; while (await e.MoveNextAsync()) { diff --git a/src/UniTask.NetCore/Linq/ToDictionary.cs b/src/UniTask.NetCore/Linq/ToDictionary.cs index c24c914..56d65e2 100644 --- a/src/UniTask.NetCore/Linq/ToDictionary.cs +++ b/src/UniTask.NetCore/Linq/ToDictionary.cs @@ -1,775 +1,278 @@ -namespace Cysharp.Threading.Tasks.Linq +using Cysharp.Threading.Tasks.Internal; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq { - internal sealed class ToDictionary + public static partial class UniTaskAsyncEnumerable { + public static UniTask> ToDictionaryAsync(this IUniTaskAsyncEnumerable source, Func keySelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return ToDictionary.InvokeAsync(source, keySelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToDictionaryAsync(this IUniTaskAsyncEnumerable source, Func keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToDictionary.InvokeAsync(source, keySelector, comparer, cancellationToken); + } + + public static UniTask> ToDictionaryAsync(this IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + + return ToDictionary.InvokeAsync(source, keySelector, elementSelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToDictionaryAsync(this IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToDictionary.InvokeAsync(source, keySelector, elementSelector, comparer, cancellationToken); + } + + public static UniTask> ToDictionaryAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return ToDictionary.InvokeAsync(source, keySelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToDictionaryAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToDictionary.InvokeAsync(source, keySelector, comparer, cancellationToken); + } + + public static UniTask> ToDictionaryAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + + return ToDictionary.InvokeAsync(source, keySelector, elementSelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToDictionaryAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToDictionary.InvokeAsync(source, keySelector, elementSelector, comparer, cancellationToken); + } + + public static UniTask> ToDictionaryAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return ToDictionary.InvokeAsync(source, keySelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToDictionaryAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToDictionary.InvokeAsync(source, keySelector, comparer, cancellationToken); + } + + public static UniTask> ToDictionaryAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + + return ToDictionary.InvokeAsync(source, keySelector, elementSelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToDictionaryAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToDictionary.InvokeAsync(source, keySelector, elementSelector, comparer, cancellationToken); + } } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + internal static class ToDictionary + { + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func keySelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var dict = new Dictionary(comparer); + + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (await e.MoveNextAsync()) + { + var v = e.Current; + var key = keySelector(v); + dict.Add(key, v); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + + return dict; + } + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var dict = new Dictionary(comparer); + + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (await e.MoveNextAsync()) + { + var v = e.Current; + var key = keySelector(v); + var value = elementSelector(v); + dict.Add(key, value); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + + return dict; + } + + // with await + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var dict = new Dictionary(comparer); + + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (await e.MoveNextAsync()) + { + var v = e.Current; + var key = await keySelector(v); + dict.Add(key, v); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + + return dict; + } + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var dict = new Dictionary(comparer); + + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (await e.MoveNextAsync()) + { + var v = e.Current; + var key = await keySelector(v); + var value = await elementSelector(v); + dict.Add(key, value); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + + return dict; + } + + // with cancellation + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var dict = new Dictionary(comparer); + + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (await e.MoveNextAsync()) + { + var v = e.Current; + var key = await keySelector(v, cancellationToken); + dict.Add(key, v); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + + return dict; + } + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var dict = new Dictionary(comparer); + + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (await e.MoveNextAsync()) + { + var v = e.Current; + var key = await keySelector(v, cancellationToken); + var value = await elementSelector(v, cancellationToken); + dict.Add(key, value); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + + return dict; + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/ToEnumerable.cs b/src/UniTask.NetCore/Linq/ToEnumerable.cs deleted file mode 100644 index 64f1070..0000000 --- a/src/UniTask.NetCore/Linq/ToEnumerable.cs +++ /dev/null @@ -1,775 +0,0 @@ -namespace Cysharp.Threading.Tasks.Linq -{ - internal sealed class ToEnumerable - { - } - - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniTask.NetCore/Linq/ToHashSet.cs b/src/UniTask.NetCore/Linq/ToHashSet.cs index 5787c40..d858db1 100644 --- a/src/UniTask.NetCore/Linq/ToHashSet.cs +++ b/src/UniTask.NetCore/Linq/ToHashSet.cs @@ -1,775 +1,42 @@ -namespace Cysharp.Threading.Tasks.Linq +using Cysharp.Threading.Tasks.Internal; +using System.Collections.Generic; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq { - internal sealed class ToHashSet + public static partial class UniTaskAsyncEnumerable { + public static UniTask> ToHashSetAsync(this IUniTaskAsyncEnumerable source, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + + return Cysharp.Threading.Tasks.Linq.ToHashSet.InvokeAsync(source, cancellationToken); + } } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + internal static class ToHashSet + { + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, CancellationToken cancellationToken) + { + var set = new HashSet(); + + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (await e.MoveNextAsync()) + { + set.Add(e.Current); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + + return set; + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/ToList.cs b/src/UniTask.NetCore/Linq/ToList.cs index 0a6dabb..07182c9 100644 --- a/src/UniTask.NetCore/Linq/ToList.cs +++ b/src/UniTask.NetCore/Linq/ToList.cs @@ -1,775 +1,42 @@ -namespace Cysharp.Threading.Tasks.Linq +using Cysharp.Threading.Tasks.Internal; +using System.Collections.Generic; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq { - internal sealed class ToList + public static partial class UniTaskAsyncEnumerable { + public static UniTask> ToListAsync(this IUniTaskAsyncEnumerable source, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + + return Cysharp.Threading.Tasks.Linq.ToList.InvokeAsync(source, cancellationToken); + } } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + internal static class ToList + { + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, CancellationToken cancellationToken) + { + var list = new List(); + + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (await e.MoveNextAsync()) + { + list.Add(e.Current); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + + return list; + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/ToLookup.cs b/src/UniTask.NetCore/Linq/ToLookup.cs index e205e65..d0ccbaa 100644 --- a/src/UniTask.NetCore/Linq/ToLookup.cs +++ b/src/UniTask.NetCore/Linq/ToLookup.cs @@ -1,775 +1,544 @@ -namespace Cysharp.Threading.Tasks.Linq +using Cysharp.Threading.Tasks.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq { - internal sealed class ToLookup + public static partial class UniTaskAsyncEnumerable { + public static UniTask> ToLookupAsync(this IUniTaskAsyncEnumerable source, Func keySelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return ToLookup.InvokeAsync(source, keySelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToLookupAsync(this IUniTaskAsyncEnumerable source, Func keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToLookup.InvokeAsync(source, keySelector, comparer, cancellationToken); + } + + public static UniTask> ToLookupAsync(this IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + + return ToLookup.InvokeAsync(source, keySelector, elementSelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToLookupAsync(this IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToLookup.InvokeAsync(source, keySelector, elementSelector, comparer, cancellationToken); + } + + public static UniTask> ToLookupAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return ToLookup.InvokeAsync(source, keySelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToLookupAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToLookup.InvokeAsync(source, keySelector, comparer, cancellationToken); + } + + public static UniTask> ToLookupAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + + return ToLookup.InvokeAsync(source, keySelector, elementSelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToLookupAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToLookup.InvokeAsync(source, keySelector, elementSelector, comparer, cancellationToken); + } + + public static UniTask> ToLookupAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return ToLookup.InvokeAsync(source, keySelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToLookupAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToLookup.InvokeAsync(source, keySelector, comparer, cancellationToken); + } + + public static UniTask> ToLookupAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + + return ToLookup.InvokeAsync(source, keySelector, elementSelector, EqualityComparer.Default, cancellationToken); + } + + public static UniTask> ToLookupAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(elementSelector, nameof(elementSelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return ToLookup.InvokeAsync(source, keySelector, elementSelector, comparer, cancellationToken); + } } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + internal static class ToLookup + { + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func keySelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var pool = ArrayPool.Shared; + var array = pool.Rent(16); + + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + var i = 0; + while (await e.MoveNextAsync()) + { + ArrayPoolUtil.EnsureCapacity(ref array, i, pool); + array[i++] = e.Current; + } + + if (i == 0) + { + return Lookup.CreateEmpty(); + } + else + { + return Lookup.Create(new ArraySegment(array, 0, i), keySelector, comparer); + } + } + finally + { + pool.Return(array, clearArray: !RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType()); + + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var pool = ArrayPool.Shared; + var array = pool.Rent(16); + + IUniTaskAsyncEnumerator e = default; + try + { + e = source.GetAsyncEnumerator(cancellationToken); + var i = 0; + while (await e.MoveNextAsync()) + { + ArrayPoolUtil.EnsureCapacity(ref array, i, pool); + array[i++] = e.Current; + } + + if (i == 0) + { + return Lookup.CreateEmpty(); + } + else + { + return Lookup.Create(new ArraySegment(array, 0, i), keySelector, elementSelector, comparer); + } + } + finally + { + pool.Return(array, clearArray: !RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType()); + + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + + // with await + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var pool = ArrayPool.Shared; + var array = pool.Rent(16); + + IUniTaskAsyncEnumerator e = default; + try + { + e = source.GetAsyncEnumerator(cancellationToken); + var i = 0; + while (await e.MoveNextAsync()) + { + ArrayPoolUtil.EnsureCapacity(ref array, i, pool); + array[i++] = e.Current; + } + + if (i == 0) + { + return Lookup.CreateEmpty(); + } + else + { + return await Lookup.CreateAsync(new ArraySegment(array, 0, i), keySelector, comparer); + } + } + finally + { + pool.Return(array, clearArray: !RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType()); + + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var pool = ArrayPool.Shared; + var array = pool.Rent(16); + + IUniTaskAsyncEnumerator e = default; + try + { + e = source.GetAsyncEnumerator(cancellationToken); + var i = 0; + while (await e.MoveNextAsync()) + { + ArrayPoolUtil.EnsureCapacity(ref array, i, pool); + array[i++] = e.Current; + } + + if (i == 0) + { + return Lookup.CreateEmpty(); + } + else + { + return await Lookup.CreateAsync(new ArraySegment(array, 0, i), keySelector, elementSelector, comparer); + } + } + finally + { + pool.Return(array, clearArray: !RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType()); + + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + // with cancellation + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var pool = ArrayPool.Shared; + var array = pool.Rent(16); + + IUniTaskAsyncEnumerator e = default; + try + { + e = source.GetAsyncEnumerator(cancellationToken); + var i = 0; + while (await e.MoveNextAsync()) + { + ArrayPoolUtil.EnsureCapacity(ref array, i, pool); + array[i++] = e.Current; + } + + if (i == 0) + { + return Lookup.CreateEmpty(); + } + else + { + return await Lookup.CreateAsync(new ArraySegment(array, 0, i), keySelector, comparer, cancellationToken); + } + } + finally + { + pool.Return(array, clearArray: !RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType()); + + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + internal static async UniTask> InvokeAsync(IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var pool = ArrayPool.Shared; + var array = pool.Rent(16); + + IUniTaskAsyncEnumerator e = default; + try + { + e = source.GetAsyncEnumerator(cancellationToken); + var i = 0; + while (await e.MoveNextAsync()) + { + ArrayPoolUtil.EnsureCapacity(ref array, i, pool); + array[i++] = e.Current; + } + + if (i == 0) + { + return Lookup.CreateEmpty(); + } + else + { + return await Lookup.CreateAsync(new ArraySegment(array, 0, i), keySelector, elementSelector, comparer, cancellationToken); + } + } + finally + { + pool.Return(array, clearArray: !RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType()); + + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + // Lookup + + class Lookup : ILookup + { + static readonly Lookup empty = new Lookup(new Dictionary>()); + + // original lookup keeps order but this impl does not(dictionary not guarantee) + readonly Dictionary> dict; + + Lookup(Dictionary> dict) + { + this.dict = dict; + } + + public static Lookup CreateEmpty() + { + return empty; + } + + public static Lookup Create(ArraySegment source, Func keySelector, IEqualityComparer comparer) + { + var dict = new Dictionary>(comparer); + + var arr = source.Array; + var c = source.Count; + for (int i = source.Offset; i < c; i++) + { + var key = keySelector(arr[i]); + + if (!dict.TryGetValue(key, out var list)) + { + list = new Grouping(key); + dict[key] = list; + } + + list.Add(arr[i]); + } + + return new Lookup(dict); + } + + public static Lookup Create(ArraySegment source, Func keySelector, Func elementSelector, IEqualityComparer comparer) + { + var dict = new Dictionary>(comparer); + + var arr = source.Array; + var c = source.Count; + for (int i = source.Offset; i < c; i++) + { + var key = keySelector(arr[i]); + var elem = elementSelector(arr[i]); + + if (!dict.TryGetValue(key, out var list)) + { + list = new Grouping(key); + dict[key] = list; + } + + list.Add(elem); + } + + return new Lookup(dict); + } + + public static async UniTask> CreateAsync(ArraySegment source, Func> keySelector, IEqualityComparer comparer) + { + var dict = new Dictionary>(comparer); + + var arr = source.Array; + var c = source.Count; + for (int i = source.Offset; i < c; i++) + { + var key = await keySelector(arr[i]); + + if (!dict.TryGetValue(key, out var list)) + { + list = new Grouping(key); + dict[key] = list; + } + + list.Add(arr[i]); + } + + return new Lookup(dict); + } + + public static async UniTask> CreateAsync(ArraySegment source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer) + { + var dict = new Dictionary>(comparer); + + var arr = source.Array; + var c = source.Count; + for (int i = source.Offset; i < c; i++) + { + var key = await keySelector(arr[i]); + var elem = await elementSelector(arr[i]); + + if (!dict.TryGetValue(key, out var list)) + { + list = new Grouping(key); + dict[key] = list; + } + + list.Add(elem); + } + + return new Lookup(dict); + } + + public static async UniTask> CreateAsync(ArraySegment source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var dict = new Dictionary>(comparer); + + var arr = source.Array; + var c = source.Count; + for (int i = source.Offset; i < c; i++) + { + var key = await keySelector(arr[i], cancellationToken); + + if (!dict.TryGetValue(key, out var list)) + { + list = new Grouping(key); + dict[key] = list; + } + + list.Add(arr[i]); + } + + return new Lookup(dict); + } + + public static async UniTask> CreateAsync(ArraySegment source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken) + { + var dict = new Dictionary>(comparer); + + var arr = source.Array; + var c = source.Count; + for (int i = source.Offset; i < c; i++) + { + var key = await keySelector(arr[i], cancellationToken); + var elem = await elementSelector(arr[i], cancellationToken); + + if (!dict.TryGetValue(key, out var list)) + { + list = new Grouping(key); + dict[key] = list; + } + + list.Add(elem); + } + + return new Lookup(dict); + } + + public IEnumerable this[TKey key] => dict[key]; + + public int Count => dict.Count; + + public bool Contains(TKey key) + { + return dict.ContainsKey(key); + } + + public IEnumerator> GetEnumerator() + { + return dict.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return dict.Values.GetEnumerator(); + } + } + + class Grouping : IGrouping + { + readonly List elements; + + public TKey Key { get; private set; } + + public Grouping(TKey key) + { + this.Key = key; + this.elements = new List(); + } + + public void Add(TElement value) + { + elements.Add(value); + } + public IEnumerator GetEnumerator() + { + return elements.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return elements.GetEnumerator(); + } + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/ToObservable.cs b/src/UniTask.NetCore/Linq/ToObservable.cs index 1f15910..4f48388 100644 --- a/src/UniTask.NetCore/Linq/ToObservable.cs +++ b/src/UniTask.NetCore/Linq/ToObservable.cs @@ -1,775 +1,97 @@ -namespace Cysharp.Threading.Tasks.Linq +using Cysharp.Threading.Tasks.Internal; +using System; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq { - internal sealed class ToObservable + public static partial class UniTaskAsyncEnumerable { + public static IObservable ToObservable(this IUniTaskAsyncEnumerable source) + { + Error.ThrowArgumentNullException(source, nameof(source)); + + return new ToObservable(source); + } } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + internal sealed class ToObservable : IObservable + { + readonly IUniTaskAsyncEnumerable source; + + public ToObservable(IUniTaskAsyncEnumerable source) + { + this.source = source; + } + + public IDisposable Subscribe(IObserver observer) + { + var ctd = new CancellationTokenDisposable(); + + RunAsync(source, observer, ctd.Token).Forget(); + + return ctd; + } + + static async UniTaskVoid RunAsync(IUniTaskAsyncEnumerable src, IObserver observer, CancellationToken cancellationToken) + { + // cancellationToken.IsCancellationRequested is called when Rx's Disposed. + // when disposed, finish silently. + + var e = src.GetAsyncEnumerator(cancellationToken); + try + { + bool hasNext; + + do + { + try + { + hasNext = await e.MoveNextAsync(); + } + catch (Exception ex) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + observer.OnError(ex); + return; + } + + if (hasNext) + { + observer.OnNext(e.Current); + } + else + { + observer.OnCompleted(); + return; + } + } while (!cancellationToken.IsCancellationRequested); + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + internal sealed class CancellationTokenDisposable : IDisposable + { + readonly CancellationTokenSource cts = new CancellationTokenSource(); + + public CancellationToken Token => cts.Token; + + public void Dispose() + { + if (!cts.IsCancellationRequested) + { + cts.Cancel(); + } + } + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/ToUniTaskAsyncEnumerable.cs b/src/UniTask.NetCore/Linq/ToUniTaskAsyncEnumerable.cs index ed452c5..435eb9c 100644 --- a/src/UniTask.NetCore/Linq/ToUniTaskAsyncEnumerable.cs +++ b/src/UniTask.NetCore/Linq/ToUniTaskAsyncEnumerable.cs @@ -48,7 +48,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, cancellationToken); } @@ -103,7 +102,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, cancellationToken); } @@ -157,7 +155,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, cancellationToken); } @@ -211,7 +208,6 @@ namespace Cysharp.Threading.Tasks.Linq public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - cancellationToken.ThrowIfCancellationRequested(); return new Enumerator(source, cancellationToken); } diff --git a/src/UniTask.NetCore/Linq/_FileMaker.cs b/src/UniTask.NetCore/Linq/_FileMaker.cs index 6754ed8..a337916 100644 --- a/src/UniTask.NetCore/Linq/_FileMaker.cs +++ b/src/UniTask.NetCore/Linq/_FileMaker.cs @@ -28,11 +28,6 @@ namespace ___Dummy throw new NotImplementedException(); } - public static IUniTaskAsyncEnumerable AsAsyncEnumerable(this IUniTaskAsyncEnumerable source) - { - throw new NotImplementedException(); - } - public static IUniTaskAsyncEnumerable Cast(this IUniTaskAsyncEnumerable source) { @@ -551,165 +546,11 @@ namespace ___Dummy throw new NotImplementedException(); } - public static IUniTaskAsyncEnumerable ToAsyncEnumerable(this IEnumerable source) - { - throw new NotImplementedException(); - } + - public static IUniTaskAsyncEnumerable ToAsyncEnumerable(this IObservable source) - { - throw new NotImplementedException(); - } - public static IUniTaskAsyncEnumerable ToAsyncEnumerable(this Task task) - { - throw new NotImplementedException(); - } + - public static UniTask> ToDictionaryAsync(this IUniTaskAsyncEnumerable source, Func keySelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAsync(this IUniTaskAsyncEnumerable source, Func keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAsync(this IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAsync(this IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToDictionaryAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static IEnumerable ToEnumerable(this IUniTaskAsyncEnumerable source) - { - throw new NotImplementedException(); - } - - public static UniTask> ToHashSetAsync(this IUniTaskAsyncEnumerable source, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToHashSetAsync(this IUniTaskAsyncEnumerable source, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToListAsync(this IUniTaskAsyncEnumerable source, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAsync(this IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAsync(this IUniTaskAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAsync(this IUniTaskAsyncEnumerable source, Func keySelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAsync(this IUniTaskAsyncEnumerable source, Func keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAwaitAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static UniTask> ToLookupAwaitWithCancellationAsync(this IUniTaskAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer comparer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public static IObservable ToObservable(this IUniTaskAsyncEnumerable source) - { - throw new NotImplementedException(); - } public static IUniTaskAsyncEnumerable Union(this IUniTaskAsyncEnumerable first, IUniTaskAsyncEnumerable second) { diff --git a/src/UniTask.NetCoreSandbox/Program.cs b/src/UniTask.NetCoreSandbox/Program.cs index 6ca3f92..6344532 100644 --- a/src/UniTask.NetCoreSandbox/Program.cs +++ b/src/UniTask.NetCoreSandbox/Program.cs @@ -31,15 +31,24 @@ namespace NetCoreSandbox } } - - static async Task Main(string[] args) + static async IAsyncEnumerable FooAsync([EnumeratorCancellation]CancellationToken cancellationToken = default) { - new int[] { }.Aggregate((x, y) => x + y); + yield return 1; + await Task.Delay(10, cancellationToken); + } + static void Main(string[] args) + { + // Create Canceled token. + var cts = new CancellationTokenSource(); + cts.Cancel(); + // OK, don't throw. + var e1 = FooAsync(cts.Token).GetAsyncEnumerator(cts.Token); + Console.WriteLine("OK:FooAsyunc().GetAsyncEnumerator()"); - - await Task.Yield(); + // Ix.Async LINQ Operator throws OperationCanceledException + var e2 = FooAsync(cts.Token).Select(x => x).GetAsyncEnumerator(cts.Token); } diff --git a/src/UniTask.NetCoreTests/Linq/Convert.cs b/src/UniTask.NetCoreTests/Linq/Convert.cs index b4fb59a..0e3d4b9 100644 --- a/src/UniTask.NetCoreTests/Linq/Convert.cs +++ b/src/UniTask.NetCoreTests/Linq/Convert.cs @@ -28,6 +28,21 @@ namespace NetCoreTests.Linq } } + + [Fact] + public async Task ToObservable() + { + { + var xs = await UniTaskAsyncEnumerable.Range(1, 10).ToObservable().ToArray(); + xs.Should().BeEquivalentTo(Enumerable.Range(1, 10)); + } + { + var xs = await UniTaskAsyncEnumerable.Range(1, 0).ToObservable().ToArray(); + xs.Should().BeEquivalentTo(Enumerable.Range(1, 0)); + } + } + + [Fact] public async Task ToAsyncEnumerableTask() { @@ -73,6 +88,108 @@ namespace NetCoreTests.Linq } } + [Fact] + public async Task ToDictionary() + { + { + var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x); + var ys = Enumerable.Range(1, 100).ToDictionary(x => x); + + xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); + } + { + var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x); + var ys = Enumerable.Range(1, 0).ToDictionary(x => x); + + xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); + } + { + var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2); + var ys = Enumerable.Range(1, 100).ToDictionary(x => x, x => x * 2); + + xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); + } + { + var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2); + var ys = Enumerable.Range(1, 0).ToDictionary(x => x, x => x * 2); + + xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); + } + } + + [Fact] + public async Task ToLookup() + { + var arr = new[] { 1, 4, 10, 10, 4, 5, 10, 9 }; + { + var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x); + var ys = arr.ToLookup(x => x); + + xs.Count.Should().Be(ys.Count); + xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); + foreach (var key in xs.Select(x => x.Key)) + { + xs[key].Should().BeEquivalentTo(ys[key]); + } + } + { + var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x); + var ys = Enumerable.Range(1, 0).ToLookup(x => x); + + xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); + } + { + var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2); + var ys = arr.ToLookup(x => x, x => x * 2); + + xs.Count.Should().Be(ys.Count); + xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); + foreach (var key in xs.Select(x => x.Key)) + { + xs[key].Should().BeEquivalentTo(ys[key]); + } + } + { + var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2); + var ys = Enumerable.Range(1, 0).ToLookup(x => x, x => x * 2); + + xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key)); + } + } + + [Fact] + public async Task ToList() + { + { + var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToListAsync(); + var ys = Enumerable.Range(1, 100).ToList(); + + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await Enumerable.Empty().ToUniTaskAsyncEnumerable().ToListAsync(); + var ys = Enumerable.Empty().ToList(); + + xs.Should().BeEquivalentTo(ys); + } + } + + [Fact] + public async Task ToHashSet() + { + { + var xs = await new[] { 1, 20, 4, 5, 20, 4, 6 }.ToUniTaskAsyncEnumerable().ToHashSetAsync(); + var ys = new[] { 1, 20, 4, 5, 20, 4, 6 }.ToHashSet(); + + xs.OrderBy(x => x).Should().BeEquivalentTo(ys.OrderBy(x => x)); + } + { + var xs = await Enumerable.Empty().ToUniTaskAsyncEnumerable().ToHashSetAsync(); + var ys = Enumerable.Empty().ToHashSet(); + + xs.Should().BeEquivalentTo(ys); + } + } }