Skip to content

Commit b398890

Browse files
authored
Merge pull request #841 from Lillenne/dev/memory-manager-offset
Include sizeof(T) in offset calculation for Memory2D from MemoryManager
2 parents e7acd48 + 395e54a commit b398890

File tree

3 files changed

+103
-8
lines changed

3 files changed

+103
-8
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ public Memory2D(MemoryManager<T> memoryManager, int height, int width)
338338
/// <exception cref="ArgumentException">
339339
/// Thrown when the requested area is outside of bounds for <paramref name="memoryManager"/>.
340340
/// </exception>
341-
public Memory2D(MemoryManager<T> memoryManager, int offset, int height, int width, int pitch)
341+
public unsafe Memory2D(MemoryManager<T> memoryManager, int offset, int height, int width, int pitch)
342342
{
343343
int length = memoryManager.GetSpan().Length;
344344

@@ -378,7 +378,7 @@ public Memory2D(MemoryManager<T> memoryManager, int offset, int height, int widt
378378
}
379379

380380
this.instance = memoryManager;
381-
this.offset = (nint)(uint)offset;
381+
this.offset = (nint)(uint)offset * (nint)(uint)sizeof(T);
382382
this.height = height;
383383
this.width = width;
384384
this.pitch = pitch;
@@ -413,7 +413,7 @@ internal Memory2D(Memory<T> memory, int height, int width)
413413
/// <exception cref="ArgumentException">
414414
/// Thrown when the requested area is outside of bounds for <paramref name="memory"/>.
415415
/// </exception>
416-
internal Memory2D(Memory<T> memory, int offset, int height, int width, int pitch)
416+
internal unsafe Memory2D(Memory<T> memory, int offset, int height, int width, int pitch)
417417
{
418418
if ((uint)offset > (uint)memory.Length)
419419
{
@@ -477,7 +477,7 @@ internal Memory2D(Memory<T> memory, int offset, int height, int width, int pitch
477477
else if (MemoryMarshal.TryGetMemoryManager<T, MemoryManager<T>>(memory, out MemoryManager<T>? memoryManager, out int memoryManagerStart, out _))
478478
{
479479
this.instance = memoryManager;
480-
this.offset = (nint)(uint)(memoryManagerStart + offset);
480+
this.offset = (nint)(uint)(memoryManagerStart + offset) * (nint)(uint)sizeof(T);
481481
}
482482
else
483483
{

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ public ReadOnlyMemory2D(MemoryManager<T> memoryManager, int height, int width)
358358
/// <exception cref="ArgumentException">
359359
/// Thrown when the requested area is outside of bounds for <paramref name="memoryManager"/>.
360360
/// </exception>
361-
public ReadOnlyMemory2D(MemoryManager<T> memoryManager, int offset, int height, int width, int pitch)
361+
public unsafe ReadOnlyMemory2D(MemoryManager<T> memoryManager, int offset, int height, int width, int pitch)
362362
{
363363
int length = memoryManager.GetSpan().Length;
364364

@@ -398,7 +398,7 @@ public ReadOnlyMemory2D(MemoryManager<T> memoryManager, int offset, int height,
398398
}
399399

400400
this.instance = memoryManager;
401-
this.offset = (nint)(uint)offset;
401+
this.offset = (nint)(uint)offset * (nint)(uint)sizeof(T);
402402
this.height = height;
403403
this.width = width;
404404
this.pitch = pitch;
@@ -433,7 +433,7 @@ internal ReadOnlyMemory2D(ReadOnlyMemory<T> memory, int height, int width)
433433
/// <exception cref="ArgumentException">
434434
/// Thrown when the requested area is outside of bounds for <paramref name="memory"/>.
435435
/// </exception>
436-
internal ReadOnlyMemory2D(ReadOnlyMemory<T> memory, int offset, int height, int width, int pitch)
436+
internal unsafe ReadOnlyMemory2D(ReadOnlyMemory<T> memory, int offset, int height, int width, int pitch)
437437
{
438438
if ((uint)offset > (uint)memory.Length)
439439
{
@@ -489,7 +489,7 @@ internal ReadOnlyMemory2D(ReadOnlyMemory<T> memory, int offset, int height, int
489489
else if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<T>? memoryManager, out int memoryManagerStart, out _))
490490
{
491491
this.instance = memoryManager;
492-
this.offset = (nint)(uint)(memoryManagerStart + offset);
492+
this.offset = (nint)(uint)(memoryManagerStart + offset) * (nint)(uint)sizeof(T);
493493
}
494494
else
495495
{

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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
#if NET6_0_OR_GREATER
7+
using System.Buffers;
8+
#endif
69
using System.Runtime.CompilerServices;
710
using CommunityToolkit.HighPerformance.Enumerables;
811
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -1014,4 +1017,96 @@ public void Test_ReadOnlySpan2DT_ReadOnlyRefEnumerable_Cast()
10141017

10151018
CollectionAssert.AreEqual(result, row);
10161019
}
1020+
1021+
#if NET6_0_OR_GREATER
1022+
[TestMethod]
1023+
public void Test_ReadOnlySpan2DT_FromMemoryManager_Indexing()
1024+
{
1025+
const int w = 10;
1026+
const int h = 10;
1027+
const int l = w * h;
1028+
1029+
byte[] b = new byte[l];
1030+
short[] s = new short[l];
1031+
1032+
for (int i = 0; i < l; ++i)
1033+
{
1034+
b[i] = (byte)i;
1035+
s[i] = (short)i;
1036+
}
1037+
1038+
Memory2DTester<byte> byteTester = new(w, h, b);
1039+
Span2D<byte> byteSpan2DFromArray = byteTester.GetMemory2DFromArray().Span;
1040+
1041+
Assert.AreEqual(11, byteSpan2DFromArray[0, 0]);
1042+
1043+
Span2D<byte> byteSpan2DFromMemoryManager = byteTester.GetMemory2DFromMemoryManager().Span;
1044+
1045+
Assert.AreEqual(11, byteSpan2DFromMemoryManager[0, 0]);
1046+
1047+
Memory2DTester<short> shortTester = new(w, h, s);
1048+
Span2D<short> shortSpan2DFromArray = shortTester.GetMemory2DFromArray().Span;
1049+
Span2D<short> shortSpan2DFromMemoryManager = shortTester.GetMemory2DFromMemoryManager().Span;
1050+
1051+
Assert.AreEqual(11, shortSpan2DFromArray[0, 0]);
1052+
Assert.AreEqual(11, shortSpan2DFromMemoryManager[0, 0]);
1053+
}
1054+
#endif
10171055
}
1056+
1057+
#if NET6_0_OR_GREATER
1058+
public sealed class Memory2DTester<T> : MemoryManager<T>
1059+
where T : unmanaged
1060+
{
1061+
private readonly T[] data;
1062+
1063+
public Memory2DTester(int w, int h, T[] data)
1064+
{
1065+
if (w < 2 || h < 2)
1066+
{
1067+
throw new ArgumentException("The 'w' and 'h' arguments must be at least 2.");
1068+
}
1069+
1070+
this.data = data;
1071+
1072+
Width = w;
1073+
Height = h;
1074+
}
1075+
1076+
public int Width { get; }
1077+
1078+
public int Height { get; }
1079+
1080+
public Memory2D<T> GetMemory2DFromMemoryManager()
1081+
{
1082+
return new(this, Width + 1, Height - 1, Width - 1, 1);
1083+
}
1084+
1085+
public Memory2D<T> GetMemory2DFromArray()
1086+
{
1087+
return new(this.data, Width + 1, Height - 1, Width - 1, 1);
1088+
}
1089+
1090+
/// <inheritdoc/>
1091+
public override Span<T> GetSpan()
1092+
{
1093+
return new(this.data);
1094+
}
1095+
1096+
/// <inheritdoc/>
1097+
public override MemoryHandle Pin(int elementIndex = 0)
1098+
{
1099+
return default;
1100+
}
1101+
1102+
/// <inheritdoc/>
1103+
public override void Unpin()
1104+
{
1105+
}
1106+
1107+
/// <inheritdoc/>
1108+
protected override void Dispose(bool disposing)
1109+
{
1110+
}
1111+
}
1112+
#endif

0 commit comments

Comments
 (0)