From 395e54a1c877161b7d432ffa798381be593aaea8 Mon Sep 17 00:00:00 2001 From: Austin Kearns Date: Wed, 21 Feb 2024 16:47:30 -0800 Subject: [PATCH] Add sizeof(T) to Memory2D offset from MemoryManager --- .../Memory/Memory2D{T}.cs | 8 +- .../Memory/ReadOnlyMemory2D{T}.cs | 8 +- .../Memory/Test_ReadOnlySpan2D{T}.cs | 95 +++++++++++++++++++ 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/CommunityToolkit.HighPerformance/Memory/Memory2D{T}.cs b/src/CommunityToolkit.HighPerformance/Memory/Memory2D{T}.cs index 2b7b09b40..848499cf8 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/Memory2D{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/Memory2D{T}.cs @@ -338,7 +338,7 @@ public Memory2D(MemoryManager memoryManager, int height, int width) /// /// Thrown when the requested area is outside of bounds for . /// - public Memory2D(MemoryManager memoryManager, int offset, int height, int width, int pitch) + public unsafe Memory2D(MemoryManager memoryManager, int offset, int height, int width, int pitch) { int length = memoryManager.GetSpan().Length; @@ -378,7 +378,7 @@ public Memory2D(MemoryManager memoryManager, int offset, int height, int widt } this.instance = memoryManager; - this.offset = (nint)(uint)offset; + this.offset = (nint)(uint)offset * (nint)(uint)sizeof(T); this.height = height; this.width = width; this.pitch = pitch; @@ -413,7 +413,7 @@ internal Memory2D(Memory memory, int height, int width) /// /// Thrown when the requested area is outside of bounds for . /// - internal Memory2D(Memory memory, int offset, int height, int width, int pitch) + internal unsafe Memory2D(Memory memory, int offset, int height, int width, int pitch) { if ((uint)offset > (uint)memory.Length) { @@ -477,7 +477,7 @@ internal Memory2D(Memory memory, int offset, int height, int width, int pitch else if (MemoryMarshal.TryGetMemoryManager>(memory, out MemoryManager? memoryManager, out int memoryManagerStart, out _)) { this.instance = memoryManager; - this.offset = (nint)(uint)(memoryManagerStart + offset); + this.offset = (nint)(uint)(memoryManagerStart + offset) * (nint)(uint)sizeof(T); } else { diff --git a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs index 7c57e620c..2786c8799 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs @@ -358,7 +358,7 @@ public ReadOnlyMemory2D(MemoryManager memoryManager, int height, int width) /// /// Thrown when the requested area is outside of bounds for . /// - public ReadOnlyMemory2D(MemoryManager memoryManager, int offset, int height, int width, int pitch) + public unsafe ReadOnlyMemory2D(MemoryManager memoryManager, int offset, int height, int width, int pitch) { int length = memoryManager.GetSpan().Length; @@ -398,7 +398,7 @@ public ReadOnlyMemory2D(MemoryManager memoryManager, int offset, int height, } this.instance = memoryManager; - this.offset = (nint)(uint)offset; + this.offset = (nint)(uint)offset * (nint)(uint)sizeof(T); this.height = height; this.width = width; this.pitch = pitch; @@ -433,7 +433,7 @@ internal ReadOnlyMemory2D(ReadOnlyMemory memory, int height, int width) /// /// Thrown when the requested area is outside of bounds for . /// - internal ReadOnlyMemory2D(ReadOnlyMemory memory, int offset, int height, int width, int pitch) + internal unsafe ReadOnlyMemory2D(ReadOnlyMemory memory, int offset, int height, int width, int pitch) { if ((uint)offset > (uint)memory.Length) { @@ -489,7 +489,7 @@ internal ReadOnlyMemory2D(ReadOnlyMemory memory, int offset, int height, int else if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager? memoryManager, out int memoryManagerStart, out _)) { this.instance = memoryManager; - this.offset = (nint)(uint)(memoryManagerStart + offset); + this.offset = (nint)(uint)(memoryManagerStart + offset) * (nint)(uint)sizeof(T); } else { diff --git a/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlySpan2D{T}.cs b/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlySpan2D{T}.cs index c7c4db13c..b65c83075 100644 --- a/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlySpan2D{T}.cs +++ b/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlySpan2D{T}.cs @@ -3,6 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +#if NET6_0_OR_GREATER +using System.Buffers; +#endif using System.Runtime.CompilerServices; using CommunityToolkit.HighPerformance.Enumerables; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -1014,4 +1017,96 @@ public void Test_ReadOnlySpan2DT_ReadOnlyRefEnumerable_Cast() CollectionAssert.AreEqual(result, row); } + +#if NET6_0_OR_GREATER + [TestMethod] + public void Test_ReadOnlySpan2DT_FromMemoryManager_Indexing() + { + const int w = 10; + const int h = 10; + const int l = w * h; + + byte[] b = new byte[l]; + short[] s = new short[l]; + + for (int i = 0; i < l; ++i) + { + b[i] = (byte)i; + s[i] = (short)i; + } + + Memory2DTester byteTester = new(w, h, b); + Span2D byteSpan2DFromArray = byteTester.GetMemory2DFromArray().Span; + + Assert.AreEqual(11, byteSpan2DFromArray[0, 0]); + + Span2D byteSpan2DFromMemoryManager = byteTester.GetMemory2DFromMemoryManager().Span; + + Assert.AreEqual(11, byteSpan2DFromMemoryManager[0, 0]); + + Memory2DTester shortTester = new(w, h, s); + Span2D shortSpan2DFromArray = shortTester.GetMemory2DFromArray().Span; + Span2D shortSpan2DFromMemoryManager = shortTester.GetMemory2DFromMemoryManager().Span; + + Assert.AreEqual(11, shortSpan2DFromArray[0, 0]); + Assert.AreEqual(11, shortSpan2DFromMemoryManager[0, 0]); + } +#endif } + +#if NET6_0_OR_GREATER +public sealed class Memory2DTester : MemoryManager + where T : unmanaged +{ + private readonly T[] data; + + public Memory2DTester(int w, int h, T[] data) + { + if (w < 2 || h < 2) + { + throw new ArgumentException("The 'w' and 'h' arguments must be at least 2."); + } + + this.data = data; + + Width = w; + Height = h; + } + + public int Width { get; } + + public int Height { get; } + + public Memory2D GetMemory2DFromMemoryManager() + { + return new(this, Width + 1, Height - 1, Width - 1, 1); + } + + public Memory2D GetMemory2DFromArray() + { + return new(this.data, Width + 1, Height - 1, Width - 1, 1); + } + + /// + public override Span GetSpan() + { + return new(this.data); + } + + /// + public override MemoryHandle Pin(int elementIndex = 0) + { + return default; + } + + /// + public override void Unpin() + { + } + + /// + protected override void Dispose(bool disposing) + { + } +} +#endif