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