Skip to content

Commit 4ad7fef

Browse files
authored
Merge pull request #675 from CommunityToolkit/dev/fix-memory2d-slicing
Fix AV when indexing a sliced Memory2D<T> backed by custom MemoryManager<T>
2 parents ceba207 + 46b52b4 commit 4ad7fef

File tree

10 files changed

+72
-22
lines changed

10 files changed

+72
-22
lines changed

src/CommunityToolkit.HighPerformance/Buffers/Internals/ArrayMemoryManager{TFrom,TTo}.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,15 @@ public override unsafe MemoryHandle Pin(int elementIndex = 0)
7575
ThrowArgumentOutOfRangeExceptionForInvalidIndex();
7676
}
7777

78-
int bytePrefix = this.offset * sizeof(TFrom);
79-
int byteSuffix = elementIndex * sizeof(TTo);
80-
int byteOffset = bytePrefix + byteSuffix;
78+
nint bytePrefix = this.offset * sizeof(TFrom);
79+
nint byteSuffix = elementIndex * sizeof(TTo);
80+
nint byteOffset = bytePrefix + byteSuffix;
8181

8282
GCHandle handle = GCHandle.Alloc(this.array, GCHandleType.Pinned);
8383

8484
ref TFrom r0 = ref this.array.DangerousGetReference();
8585
ref byte r1 = ref Unsafe.As<TFrom, byte>(ref r0);
86-
ref byte r2 = ref Unsafe.Add(ref r1, byteOffset);
86+
ref byte r2 = ref Unsafe.AddByteOffset(ref r1, byteOffset);
8787
void* pi = Unsafe.AsPointer(ref r2);
8888

8989
return new(pi, handle);

src/CommunityToolkit.HighPerformance/Buffers/Internals/StringMemoryManager{TTo}.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,15 @@ public override unsafe MemoryHandle Pin(int elementIndex = 0)
7171
ThrowArgumentOutOfRangeExceptionForInvalidIndex();
7272
}
7373

74-
int bytePrefix = this.offset * sizeof(char);
75-
int byteSuffix = elementIndex * sizeof(TTo);
76-
int byteOffset = bytePrefix + byteSuffix;
74+
nint bytePrefix = this.offset * sizeof(char);
75+
nint byteSuffix = elementIndex * sizeof(TTo);
76+
nint byteOffset = bytePrefix + byteSuffix;
7777

7878
GCHandle handle = GCHandle.Alloc(this.text, GCHandleType.Pinned);
7979

8080
ref char r0 = ref this.text.DangerousGetReference();
8181
ref byte r1 = ref Unsafe.As<char, byte>(ref r0);
82-
ref byte r2 = ref Unsafe.Add(ref r1, byteOffset);
82+
ref byte r2 = ref Unsafe.AddByteOffset(ref r1, byteOffset);
8383
void* pi = Unsafe.AsPointer(ref r2);
8484

8585
return new(pi, handle);

src/CommunityToolkit.HighPerformance/Memory/Memory2D{T}.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ namespace CommunityToolkit.HighPerformance;
3737
private readonly object? instance;
3838

3939
/// <summary>
40-
/// The initial offset within <see cref="instance"/>.
40+
/// The initial byte offset within <see cref="instance"/>.
4141
/// </summary>
42-
private readonly IntPtr offset;
42+
private readonly nint offset;
4343

4444
/// <summary>
4545
/// The height of the specified 2D region.
@@ -603,7 +603,7 @@ public Span2D<T> Span
603603
if (this.instance is MemoryManager<T> memoryManager)
604604
{
605605
ref T r0 = ref memoryManager.GetSpan().DangerousGetReference();
606-
ref T r1 = ref Unsafe.Add(ref r0, this.offset);
606+
ref T r1 = ref Unsafe.AddByteOffset(ref r0, this.offset);
607607

608608
return new(ref r1, this.height, this.width, this.pitch);
609609
}

src/CommunityToolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ namespace CommunityToolkit.HighPerformance;
3434
private readonly object? instance;
3535

3636
/// <summary>
37-
/// The initial offset within <see cref="instance"/>.
37+
/// The initial byte offset within <see cref="instance"/>.
3838
/// </summary>
39-
private readonly IntPtr offset;
39+
private readonly nint offset;
4040

4141
/// <summary>
4242
/// The height of the specified 2D region.
@@ -615,7 +615,7 @@ public ReadOnlySpan2D<T> Span
615615
if (this.instance is MemoryManager<T> memoryManager)
616616
{
617617
ref T r0 = ref memoryManager.GetSpan().DangerousGetReference();
618-
ref T r1 = ref Unsafe.Add(ref r0, this.offset);
618+
ref T r1 = ref Unsafe.AddByteOffset(ref r0, this.offset);
619619

620620
return new(in r1, this.height, this.width, this.pitch);
621621
}

src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ public ref struct Enumerator
109109
private readonly object? instance;
110110

111111
/// <summary>
112-
/// The initial offset within <see cref="instance"/>.
112+
/// The initial byte offset within <see cref="instance"/>.
113113
/// </summary>
114-
private readonly IntPtr offset;
114+
private readonly nint offset;
115115

116116
/// <summary>
117117
/// The height of the specified 2D region.

src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ public readonly ref partial struct ReadOnlySpan2D<T>
5050
private readonly object? instance;
5151

5252
/// <summary>
53-
/// The initial offset within <see cref="instance"/>.
53+
/// The initial byte offset within <see cref="instance"/>.
5454
/// </summary>
55-
private readonly IntPtr offset;
55+
private readonly nint offset;
5656

5757
/// <summary>
5858
/// The height of the specified 2D region.

src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ public ref struct Enumerator
109109
private readonly object? instance;
110110

111111
/// <summary>
112-
/// The initial offset within <see cref="instance"/>.
112+
/// The initial byte offset within <see cref="instance"/>.
113113
/// </summary>
114-
private readonly IntPtr offset;
114+
private readonly nint offset;
115115

116116
/// <summary>
117117
/// The height of the specified 2D region.

src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ public readonly ref partial struct Span2D<T>
8282
internal readonly object? Instance;
8383

8484
/// <summary>
85-
/// The initial offset within <see cref="Instance"/>.
85+
/// The initial byte offset within <see cref="Instance"/>.
8686
/// </summary>
87-
internal readonly IntPtr Offset;
87+
internal readonly nint Offset;
8888

8989
/// <summary>
9090
/// The height of the specified 2D region.

tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_Memory2D{T}.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,4 +516,29 @@ public void Test_Memory2DT_ToString()
516516

517517
Assert.AreEqual(text, expected);
518518
}
519+
520+
#if NET6_0_OR_GREATER
521+
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/3536
522+
[TestMethod]
523+
[DataRow(720, 1280)]
524+
public void Test_Memory2DT_CastAndSlice_WorksCorrectly(int height, int width)
525+
{
526+
Memory2D<int> data =
527+
new byte[width * height * sizeof(int)]
528+
.AsMemory()
529+
.Cast<byte, int>()
530+
.AsMemory2D(height: height, width: width);
531+
532+
Memory2D<int> slice = data.Slice(
533+
row: height / 2,
534+
column: 0,
535+
height: height / 2,
536+
width: width);
537+
538+
Assert.IsTrue(Unsafe.AreSame(ref data.Span[height / 2, 0], ref slice.Span[0, 0]));
539+
Assert.IsTrue(Unsafe.AreSame(ref data.Span[height / 2, width - 1], ref slice.Span[0, width - 1]));
540+
Assert.IsTrue(Unsafe.AreSame(ref data.Span[height - 1, 0], ref slice.Span[(height / 2) - 1, 0]));
541+
Assert.IsTrue(Unsafe.AreSame(ref data.Span[height - 1, width - 1], ref slice.Span[(height / 2) - 1, width - 1]));
542+
}
543+
#endif
519544
}

tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlyMemory2D{T}.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,4 +464,29 @@ public void Test_ReadOnlyMemory2DT_ToString()
464464

465465
Assert.AreEqual(text, expected);
466466
}
467+
468+
#if NET6_0_OR_GREATER
469+
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/3536
470+
[TestMethod]
471+
[DataRow(720, 1280)]
472+
public void Test_ReadOnlyMemory2DT_CastAndSlice_WorksCorrectly(int height, int width)
473+
{
474+
ReadOnlyMemory2D<int> data =
475+
new byte[width * height * sizeof(int)]
476+
.AsMemory()
477+
.Cast<byte, int>()
478+
.AsMemory2D(height: height, width: width);
479+
480+
ReadOnlyMemory2D<int> slice = data.Slice(
481+
row: height / 2,
482+
column: 0,
483+
height: height / 2,
484+
width: width);
485+
486+
Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height / 2, 0]), ref Unsafe.AsRef(in slice.Span[0, 0])));
487+
Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height / 2, width - 1]), ref Unsafe.AsRef(in slice.Span[0, width - 1])));
488+
Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height - 1, 0]), ref Unsafe.AsRef(in slice.Span[(height / 2) - 1, 0])));
489+
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])));
490+
}
491+
#endif
467492
}

0 commit comments

Comments
 (0)