.NET-Community-Toolkit/CommunityToolkit.HighPerfor.../Helpers/Internals/RefEnumerableHelper.cs

264 lines
12 KiB
C#

// 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.Runtime.CompilerServices;
namespace CommunityToolkit.HighPerformance.Helpers.Internals;
/// <summary>
/// Helpers to process sequences of values by reference with a given step.
/// </summary>
internal static class RefEnumerableHelper
{
/// <summary>
/// Clears a target memory area.
/// </summary>
/// <typeparam name="T">The type of values to clear.</typeparam>
/// <param name="r0">A <typeparamref name="T"/> reference to the start of the memory area.</param>
/// <param name="length">The number of items in the memory area.</param>
/// <param name="step">The number of items between each consecutive target value.</param>
public static void Clear<T>(ref T r0, nint length, nint step)
{
nint offset = 0;
// Main loop with 8 unrolled iterations
while (length >= 8)
{
Unsafe.Add(ref r0, offset) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
length -= 8;
offset += step;
}
if (length >= 4)
{
Unsafe.Add(ref r0, offset) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
Unsafe.Add(ref r0, offset += step) = default!;
length -= 4;
offset += step;
}
// Clear the remaining values
while (length > 0)
{
Unsafe.Add(ref r0, offset) = default!;
length -= 1;
offset += step;
}
}
/// <summary>
/// Copies a sequence of discontiguous items from one memory area to another.
/// </summary>
/// <typeparam name="T">The type of items to copy.</typeparam>
/// <param name="sourceRef">The source reference to copy from.</param>
/// <param name="destinationRef">The target reference to copy to.</param>
/// <param name="length">The total number of items to copy.</param>
/// <param name="sourceStep">The step between consecutive items in the memory area pointed to by <paramref name="sourceRef"/>.</param>
public static void CopyTo<T>(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep)
{
nint sourceOffset = 0;
nint destinationOffset = 0;
while (length >= 8)
{
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 4) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 5) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 6) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 7) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
length -= 8;
sourceOffset += sourceStep;
destinationOffset += 8;
}
if (length >= 4)
{
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
length -= 4;
sourceOffset += sourceStep;
destinationOffset += 4;
}
while (length > 0)
{
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
length -= 1;
sourceOffset += sourceStep;
destinationOffset += 1;
}
}
/// <summary>
/// Copies a sequence of discontiguous items from one memory area to another.
/// </summary>
/// <typeparam name="T">The type of items to copy.</typeparam>
/// <param name="sourceRef">The source reference to copy from.</param>
/// <param name="destinationRef">The target reference to copy to.</param>
/// <param name="length">The total number of items to copy.</param>
/// <param name="sourceStep">The step between consecutive items in the memory area pointed to by <paramref name="sourceRef"/>.</param>
/// <param name="destinationStep">The step between consecutive items in the memory area pointed to by <paramref name="destinationRef"/>.</param>
public static void CopyTo<T>(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep, nint destinationStep)
{
nint sourceOffset = 0;
nint destinationOffset = 0;
while (length >= 8)
{
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
length -= 8;
sourceOffset += sourceStep;
destinationOffset += destinationStep;
}
if (length >= 4)
{
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
length -= 4;
sourceOffset += sourceStep;
destinationOffset += destinationStep;
}
while (length > 0)
{
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
length -= 1;
sourceOffset += sourceStep;
destinationOffset += destinationStep;
}
}
/// <summary>
/// Copies a sequence of discontiguous items from one memory area to another. This mirrors
/// <see cref="CopyTo{T}(ref T,ref T,nint,nint)"/>, but <paramref name="sourceStep"/> refers to <paramref name="destinationRef"/> instead.
/// </summary>
/// <typeparam name="T">The type of items to copy.</typeparam>
/// <param name="sourceRef">The source reference to copy from.</param>
/// <param name="destinationRef">The target reference to copy to.</param>
/// <param name="length">The total number of items to copy.</param>
/// <param name="sourceStep">The step between consecutive items in the memory area pointed to by <paramref name="sourceRef"/>.</param>
public static void CopyFrom<T>(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep)
{
nint sourceOffset = 0;
nint destinationOffset = 0;
while (length >= 8)
{
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 1);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 2);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 3);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 4);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 5);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 6);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 7);
length -= 8;
sourceOffset += 8;
destinationOffset += sourceStep;
}
if (length >= 4)
{
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 1);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 2);
Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 3);
length -= 4;
sourceOffset += 4;
destinationOffset += sourceStep;
}
while (length > 0)
{
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
length -= 1;
sourceOffset += 1;
destinationOffset += sourceStep;
}
}
/// <summary>
/// Fills a target memory area.
/// </summary>
/// <typeparam name="T">The type of values to fill.</typeparam>
/// <param name="r0">A <typeparamref name="T"/> reference to the start of the memory area.</param>
/// <param name="length">The number of items in the memory area.</param>
/// <param name="step">The number of items between each consecutive target value.</param>
/// <param name="value">The value to assign to every item in the target memory area.</param>
public static void Fill<T>(ref T r0, nint length, nint step, T value)
{
nint offset = 0;
while (length >= 8)
{
Unsafe.Add(ref r0, offset) = value;
Unsafe.Add(ref r0, offset += step) = value;
Unsafe.Add(ref r0, offset += step) = value;
Unsafe.Add(ref r0, offset += step) = value;
Unsafe.Add(ref r0, offset += step) = value;
Unsafe.Add(ref r0, offset += step) = value;
Unsafe.Add(ref r0, offset += step) = value;
Unsafe.Add(ref r0, offset += step) = value;
length -= 8;
offset += step;
}
if (length >= 4)
{
Unsafe.Add(ref r0, offset) = value;
Unsafe.Add(ref r0, offset += step) = value;
Unsafe.Add(ref r0, offset += step) = value;
Unsafe.Add(ref r0, offset += step) = value;
length -= 4;
offset += step;
}
while (length > 0)
{
Unsafe.Add(ref r0, offset) = value;
length -= 1;
offset += step;
}
}
}