mirror of
https://github.com/CommunityToolkit/dotnet.git
synced 2026-10-28 11:06:40 +08:00
Added Memory<T>.AsMemory2D extensions
This commit is contained in:
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Toolkit.HighPerformance.Memory;
|
||||
using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream;
|
||||
|
||||
namespace Microsoft.Toolkit.HighPerformance.Extensions
|
||||
@@ -15,6 +16,50 @@ namespace Microsoft.Toolkit.HighPerformance.Extensions
|
||||
/// </summary>
|
||||
public static class MemoryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Memory2D{T}"/> instance wrapping the underlying data for the given <see cref="Memory{T}"/> instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of items in the input <see cref="Memory{T}"/> instance.</typeparam>
|
||||
/// <param name="memory">The input <see cref="Memory{T}"/> instance.</param>
|
||||
/// <param name="height">The height of the resulting 2D area.</param>
|
||||
/// <param name="width">The width of each row in the resulting 2D area.</param>
|
||||
/// <returns>The resulting <see cref="Memory2D{T}"/> instance.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when one of the input parameters is out of range.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when the requested area is outside of bounds for <paramref name="memory"/>.
|
||||
/// </exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Memory2D<T> AsMemory2D<T>(this Memory<T> memory, int height, int width)
|
||||
{
|
||||
return new Memory2D<T>(memory, height, width);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Memory2D{T}"/> instance wrapping the underlying data for the given <see cref="Memory{T}"/> instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of items in the input <see cref="Memory{T}"/> instance.</typeparam>
|
||||
/// <param name="memory">The input <see cref="Memory{T}"/> instance.</param>
|
||||
/// <param name="offset">The initial offset within <paramref name="memory"/>.</param>
|
||||
/// <param name="height">The height of the resulting 2D area.</param>
|
||||
/// <param name="width">The width of each row in the resulting 2D area.</param>
|
||||
/// <param name="pitch">The pitch in the resulting 2D area.</param>
|
||||
/// <returns>The resulting <see cref="Memory2D{T}"/> instance.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when one of the input parameters is out of range.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when the requested area is outside of bounds for <paramref name="memory"/>.
|
||||
/// </exception>
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Memory2D<T> AsMemory2D<T>(this Memory<T> memory, int offset, int height, int width, int pitch)
|
||||
{
|
||||
return new Memory2D<T>(memory, offset, height, width, pitch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Stream"/> wrapping the contents of the given <see cref="Memory{T}"/> of <see cref="byte"/> instance.
|
||||
/// </summary>
|
||||
|
||||
@@ -382,6 +382,118 @@ namespace Microsoft.Toolkit.HighPerformance.Memory
|
||||
this.width = width;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Memory2D{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="memory">The target <see cref="Memory{T}"/> to wrap.</param>
|
||||
/// <param name="height">The height of the resulting 2D area.</param>
|
||||
/// <param name="width">The width of each row in the resulting 2D area.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when either <paramref name="height"/> or <paramref name="width"/> are invalid.
|
||||
/// </exception>
|
||||
/// <remarks>The total area must match the lenght of <paramref name="memory"/>.</remarks>
|
||||
internal Memory2D(Memory<T> memory, int height, int width)
|
||||
: this(memory, 0, height, width, 0)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Memory2D{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="memory">The target <see cref="Memory{T}"/> to wrap.</param>
|
||||
/// <param name="offset">The initial offset within <paramref name="memory"/>.</param>
|
||||
/// <param name="height">The height of the resulting 2D area.</param>
|
||||
/// <param name="width">The width of each row in the resulting 2D area.</param>
|
||||
/// <param name="pitch">The pitch in the resulting 2D area.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when one of the input parameters is out of range.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when the requested area is outside of bounds for <paramref name="memory"/>.
|
||||
/// </exception>
|
||||
internal Memory2D(Memory<T> memory, int offset, int height, int width, int pitch)
|
||||
{
|
||||
if ((uint)offset > (uint)memory.Length)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset();
|
||||
}
|
||||
|
||||
if (height < 0)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight();
|
||||
}
|
||||
|
||||
if (width < 0)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth();
|
||||
}
|
||||
|
||||
if (pitch < 0)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch();
|
||||
}
|
||||
|
||||
if (width == 0 || height == 0)
|
||||
{
|
||||
this = default;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
remaining = memory.Length - offset,
|
||||
area = ((width + pitch) * (height - 1)) + width;
|
||||
|
||||
if (area > remaining)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentException();
|
||||
}
|
||||
|
||||
// Check if the Memory<T> instance wraps a string. This is possible in case
|
||||
// consumers do an unsafe cast for the entire Memory<T> object, and while not
|
||||
// really safe it is still supported in CoreCLR too, so we're following suit here.
|
||||
if (typeof(T) == typeof(char) &&
|
||||
MemoryMarshal.TryGetString(Unsafe.As<Memory<T>, Memory<char>>(ref memory), out string? text, out int textStart, out _))
|
||||
{
|
||||
ref char r0 = ref text.DangerousGetReferenceAt(textStart + offset);
|
||||
|
||||
this.instance = text;
|
||||
this.offset = text.DangerousGetObjectDataByteOffset(ref r0);
|
||||
}
|
||||
else if (MemoryMarshal.TryGetArray(memory, out ArraySegment<T> segment))
|
||||
{
|
||||
// Check if the input Memory<T> instance wraps an array we can access.
|
||||
// This is fine, since Memory<T> on its own doesn't control the lifetime
|
||||
// of the underlying array anyway, and this Memory2D<T> type would do the same.
|
||||
// Using the array directly makes retrieving a Span2D<T> faster down the line,
|
||||
// as we no longer have to jump through the boxed Memory<T> first anymore.
|
||||
T[] array = segment.Array!;
|
||||
|
||||
this.instance = array;
|
||||
this.offset = array.DangerousGetObjectDataByteOffset(ref array.DangerousGetReferenceAt(segment.Offset + offset));
|
||||
}
|
||||
else if (MemoryMarshal.TryGetMemoryManager<T, MemoryManager<T>>(memory, out var memoryManager, out int memoryManagerStart, out _))
|
||||
{
|
||||
this.instance = memoryManager;
|
||||
|
||||
unsafe
|
||||
{
|
||||
this.offset = (IntPtr)(void*)(uint)(memoryManagerStart + offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowHelper.ThrowArgumentExceptionForUnsupportedType();
|
||||
|
||||
this.instance = null;
|
||||
this.offset = default;
|
||||
}
|
||||
|
||||
this.height = height;
|
||||
this.width = width;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user