diff --git a/src/CommunityToolkit.HighPerformance/Buffers/Internals/ArrayMemoryManager{TFrom,TTo}.cs b/src/CommunityToolkit.HighPerformance/Buffers/Internals/ArrayMemoryManager{TFrom,TTo}.cs index dc80abf49..6866153e0 100644 --- a/src/CommunityToolkit.HighPerformance/Buffers/Internals/ArrayMemoryManager{TFrom,TTo}.cs +++ b/src/CommunityToolkit.HighPerformance/Buffers/Internals/ArrayMemoryManager{TFrom,TTo}.cs @@ -75,15 +75,15 @@ public override unsafe MemoryHandle Pin(int elementIndex = 0) ThrowArgumentOutOfRangeExceptionForInvalidIndex(); } - int bytePrefix = this.offset * sizeof(TFrom); - int byteSuffix = elementIndex * sizeof(TTo); - int byteOffset = bytePrefix + byteSuffix; + nint bytePrefix = this.offset * sizeof(TFrom); + nint byteSuffix = elementIndex * sizeof(TTo); + nint byteOffset = bytePrefix + byteSuffix; GCHandle handle = GCHandle.Alloc(this.array, GCHandleType.Pinned); ref TFrom r0 = ref this.array.DangerousGetReference(); ref byte r1 = ref Unsafe.As(ref r0); - ref byte r2 = ref Unsafe.Add(ref r1, byteOffset); + ref byte r2 = ref Unsafe.AddByteOffset(ref r1, byteOffset); void* pi = Unsafe.AsPointer(ref r2); return new(pi, handle); diff --git a/src/CommunityToolkit.HighPerformance/Buffers/Internals/StringMemoryManager{TTo}.cs b/src/CommunityToolkit.HighPerformance/Buffers/Internals/StringMemoryManager{TTo}.cs index 430e5af50..6e469a29d 100644 --- a/src/CommunityToolkit.HighPerformance/Buffers/Internals/StringMemoryManager{TTo}.cs +++ b/src/CommunityToolkit.HighPerformance/Buffers/Internals/StringMemoryManager{TTo}.cs @@ -71,15 +71,15 @@ public override unsafe MemoryHandle Pin(int elementIndex = 0) ThrowArgumentOutOfRangeExceptionForInvalidIndex(); } - int bytePrefix = this.offset * sizeof(char); - int byteSuffix = elementIndex * sizeof(TTo); - int byteOffset = bytePrefix + byteSuffix; + nint bytePrefix = this.offset * sizeof(char); + nint byteSuffix = elementIndex * sizeof(TTo); + nint byteOffset = bytePrefix + byteSuffix; GCHandle handle = GCHandle.Alloc(this.text, GCHandleType.Pinned); ref char r0 = ref this.text.DangerousGetReference(); ref byte r1 = ref Unsafe.As(ref r0); - ref byte r2 = ref Unsafe.Add(ref r1, byteOffset); + ref byte r2 = ref Unsafe.AddByteOffset(ref r1, byteOffset); void* pi = Unsafe.AsPointer(ref r2); return new(pi, handle); diff --git a/src/CommunityToolkit.HighPerformance/Memory/Memory2D{T}.cs b/src/CommunityToolkit.HighPerformance/Memory/Memory2D{T}.cs index 6c3cf348b..462998d09 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/Memory2D{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/Memory2D{T}.cs @@ -37,9 +37,9 @@ namespace CommunityToolkit.HighPerformance; private readonly object? instance; /// - /// The initial offset within . + /// The initial byte offset within . /// - private readonly IntPtr offset; + private readonly nint offset; /// /// The height of the specified 2D region. @@ -603,7 +603,7 @@ public Span2D Span if (this.instance is MemoryManager memoryManager) { ref T r0 = ref memoryManager.GetSpan().DangerousGetReference(); - ref T r1 = ref Unsafe.Add(ref r0, this.offset); + ref T r1 = ref Unsafe.AddByteOffset(ref r0, this.offset); return new(ref r1, this.height, this.width, this.pitch); } diff --git a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs index 105b1fcfc..5bc054092 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs @@ -34,9 +34,9 @@ namespace CommunityToolkit.HighPerformance; private readonly object? instance; /// - /// The initial offset within . + /// The initial byte offset within . /// - private readonly IntPtr offset; + private readonly nint offset; /// /// The height of the specified 2D region. @@ -615,7 +615,7 @@ public ReadOnlySpan2D Span if (this.instance is MemoryManager memoryManager) { ref T r0 = ref memoryManager.GetSpan().DangerousGetReference(); - ref T r1 = ref Unsafe.Add(ref r0, this.offset); + ref T r1 = ref Unsafe.AddByteOffset(ref r0, this.offset); return new(in r1, this.height, this.width, this.pitch); } diff --git a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs index 6b55632bd..0acd16c43 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs @@ -109,9 +109,9 @@ public ref struct Enumerator private readonly object? instance; /// - /// The initial offset within . + /// The initial byte offset within . /// - private readonly IntPtr offset; + private readonly nint offset; /// /// The height of the specified 2D region. diff --git a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs index e824c22be..27110c3a2 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs @@ -50,9 +50,9 @@ public readonly ref partial struct ReadOnlySpan2D private readonly object? instance; /// - /// The initial offset within . + /// The initial byte offset within . /// - private readonly IntPtr offset; + private readonly nint offset; /// /// The height of the specified 2D region. diff --git a/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs b/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs index eac6b07b0..1fda07c9f 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs @@ -109,9 +109,9 @@ public ref struct Enumerator private readonly object? instance; /// - /// The initial offset within . + /// The initial byte offset within . /// - private readonly IntPtr offset; + private readonly nint offset; /// /// The height of the specified 2D region. diff --git a/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.cs b/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.cs index 2a32c970c..11e4ab162 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.cs @@ -82,9 +82,9 @@ public readonly ref partial struct Span2D internal readonly object? Instance; /// - /// The initial offset within . + /// The initial byte offset within . /// - internal readonly IntPtr Offset; + internal readonly nint Offset; /// /// The height of the specified 2D region. diff --git a/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_Memory2D{T}.cs b/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_Memory2D{T}.cs index d1e365876..337663c71 100644 --- a/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_Memory2D{T}.cs +++ b/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_Memory2D{T}.cs @@ -516,4 +516,29 @@ public void Test_Memory2DT_ToString() Assert.AreEqual(text, expected); } + +#if NET6_0_OR_GREATER + // See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/3536 + [TestMethod] + [DataRow(720, 1280)] + public void Test_Memory2DT_CastAndSlice_WorksCorrectly(int height, int width) + { + Memory2D data = + new byte[width * height * sizeof(int)] + .AsMemory() + .Cast() + .AsMemory2D(height: height, width: width); + + Memory2D slice = data.Slice( + row: height / 2, + column: 0, + height: height / 2, + width: width); + + Assert.IsTrue(Unsafe.AreSame(ref data.Span[height / 2, 0], ref slice.Span[0, 0])); + Assert.IsTrue(Unsafe.AreSame(ref data.Span[height / 2, width - 1], ref slice.Span[0, width - 1])); + Assert.IsTrue(Unsafe.AreSame(ref data.Span[height - 1, 0], ref slice.Span[(height / 2) - 1, 0])); + Assert.IsTrue(Unsafe.AreSame(ref data.Span[height - 1, width - 1], ref slice.Span[(height / 2) - 1, width - 1])); + } +#endif } diff --git a/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlyMemory2D{T}.cs b/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlyMemory2D{T}.cs index 55cafefcb..7baa91e41 100644 --- a/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlyMemory2D{T}.cs +++ b/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlyMemory2D{T}.cs @@ -464,4 +464,29 @@ public void Test_ReadOnlyMemory2DT_ToString() Assert.AreEqual(text, expected); } + +#if NET6_0_OR_GREATER + // See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/3536 + [TestMethod] + [DataRow(720, 1280)] + public void Test_ReadOnlyMemory2DT_CastAndSlice_WorksCorrectly(int height, int width) + { + ReadOnlyMemory2D data = + new byte[width * height * sizeof(int)] + .AsMemory() + .Cast() + .AsMemory2D(height: height, width: width); + + ReadOnlyMemory2D slice = data.Slice( + row: height / 2, + column: 0, + height: height / 2, + width: width); + + Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height / 2, 0]), ref Unsafe.AsRef(in slice.Span[0, 0]))); + Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height / 2, width - 1]), ref Unsafe.AsRef(in slice.Span[0, width - 1]))); + Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height - 1, 0]), ref Unsafe.AsRef(in slice.Span[(height / 2) - 1, 0]))); + Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height - 1, width - 1]), ref Unsafe.AsRef(in slice.Span[(height / 2) - 1, width - 1]))); + } +#endif }