/* * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) * See https://github.com/openiddict/openiddict-core for more information concerning * the license and the contributors participating to this project. */ using System.Diagnostics; namespace OpenIddict.Extensions; /// /// Exposes common polyfills used by the OpenIddict assemblies. /// internal static class OpenIddictPolyfills { extension(IEnumerable source) { #if !SUPPORTS_CHUNK_LINQ_EXTENSION /// /// Split the elements of a sequence into chunks of size at most . /// /// /// Every chunk except the last will be of size . /// The last chunk will contain the remaining elements and may be of a smaller size. /// /// Maximum size of each chunk. /// /// An that contains the elements of the input /// sequence split into chunks of size . /// public IEnumerable Chunk(int size) { // Note: this polyfill was directly copied from .NET's source code: // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/Chunk.cs. using IEnumerator enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) { var count = Math.Min(size, 4); int index; do { var array = new TSource[count]; array[0] = enumerator.Current; index = 1; if (size != array.Length) { for (; index < size && enumerator.MoveNext(); index++) { if (index >= array.Length) { count = (int) Math.Min((uint) size, 2 * (uint) array.Length); Array.Resize(ref array, count); } array[index] = enumerator.Current; } } else { var local = array; Debug.Assert(local.Length == size); for (; (uint) index < (uint) local.Length && enumerator.MoveNext(); index++) { local[index] = enumerator.Current; } } if (index != array.Length) { Array.Resize(ref array, index); } yield return array; } while (index >= size && enumerator.MoveNext()); } } #endif #if !SUPPORTS_TOHASHSET_LINQ_EXTENSION /// /// Creates a new instance and imports the elements present in the specified source. /// /// The comparer to use. /// A new instance and imports the elements present in the specified source. public HashSet ToHashSet(IEqualityComparer? comparer) => new(source, comparer); #endif } extension(Task task) { #if !SUPPORTS_TASK_WAIT_ASYNC /// /// Waits until the specified task returns a result or the cancellation token is signaled. /// /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// The specified is signaled. public async Task WaitAsync(CancellationToken cancellationToken) { var source = new TaskCompletionSource(TaskCreationOptions.None); using (cancellationToken.Register(static state => ((TaskCompletionSource) state!).SetResult(true), source)) { if (await Task.WhenAny(task, source.Task) == source.Task) { throw new OperationCanceledException(cancellationToken); } await task; } } #endif } extension(Task task) { #if !SUPPORTS_TASK_WAIT_ASYNC /// /// Waits until the specified task returns a result or the cancellation token is signaled. /// /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// The specified is signaled. public async Task WaitAsync(CancellationToken cancellationToken) { var source = new TaskCompletionSource(TaskCreationOptions.None); using (cancellationToken.Register(static state => ((TaskCompletionSource) state!).SetResult(true), source)) { if (await Task.WhenAny(task, source.Task) == source.Task) { throw new OperationCanceledException(cancellationToken); } return await task; } } #endif } extension(ValueTask) { #if !SUPPORTS_VALUETASK_COMPLETED_TASK /// /// Gets a task that has already completed successfully. /// public static ValueTask CompletedTask => default; #endif } extension(ValueTask) { #if !SUPPORTS_VALUETASK_COMPLETED_TASK /// /// Gets a task that has already completed successfully. /// public static ValueTask CompletedTask => default; #endif } }