Skip to content

Commit 9964e66

Browse files
committed
Code refactoring to support Memory<T> tests
1 parent 26a9078 commit 9964e66

File tree

5 files changed

+92
-45
lines changed

5 files changed

+92
-45
lines changed

UnitTests/UnitTests.HighPerformance.Shared/Buffers/Internals/UnmanagedSpanOwner.cs

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

55
using System;
6+
using System.Buffers;
67
using System.Runtime.CompilerServices;
78
using System.Runtime.InteropServices;
89

@@ -12,7 +13,7 @@ namespace UnitTests.HighPerformance.Shared.Buffers.Internals
1213
/// An owner for a buffer of an unmanaged type, recycling <see cref="byte"/> arrays to save memory.
1314
/// </summary>
1415
/// <typeparam name="T">The type of items to store in the rented buffers.</typeparam>
15-
internal sealed unsafe class UnmanagedSpanOwner<T> : IDisposable
16+
internal sealed unsafe class UnmanagedSpanOwner<T> : MemoryManager<T>
1617
where T : unmanaged
1718
{
1819
/// <summary>
@@ -45,13 +46,8 @@ public UnmanagedSpanOwner(int size)
4546
/// </summary>
4647
public T* Ptr => (T*)this.ptr;
4748

48-
/// <summary>
49-
/// Gets the <see cref="Memory{T}"/> for the current instance.
50-
/// </summary>
51-
public Span<T> Span => new Span<T>((void*)this.ptr, this.length);
52-
5349
/// <inheritdoc/>
54-
public void Dispose()
50+
protected override void Dispose(bool disposing)
5551
{
5652
IntPtr ptr = this.ptr;
5753

@@ -64,5 +60,23 @@ public void Dispose()
6460

6561
Marshal.FreeHGlobal(ptr);
6662
}
63+
64+
/// <inheritdoc/>
65+
public override Span<T> GetSpan()
66+
{
67+
return new Span<T>((void*)this.ptr, this.length);
68+
}
69+
70+
/// <inheritdoc/>
71+
public override MemoryHandle Pin(int elementIndex = 0)
72+
{
73+
throw new NotImplementedException();
74+
}
75+
76+
/// <inheritdoc/>
77+
public override void Unpin()
78+
{
79+
throw new NotImplementedException();
80+
}
6781
}
6882
}

UnitTests/UnitTests.HighPerformance.Shared/Extensions/Test_ReadOnlySpanExtensions.Count.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ private static void TestForType<T>(T value, Func<int, T, UnmanagedSpanOwner<T>>
178178
{
179179
using UnmanagedSpanOwner<T> data = provider(count, value);
180180

181-
int result = data.Span.Count(value);
182-
int expected = CountWithForeach(data.Span, value);
181+
int result = data.GetSpan().Count(value);
182+
int expected = CountWithForeach(data.GetSpan(), value);
183183

184184
Assert.AreEqual(result, expected, $"Failed {typeof(T)} test with count {count}: got {result} instead of {expected}");
185185
}
@@ -224,15 +224,15 @@ private static UnmanagedSpanOwner<T> CreateRandomData<T>(int count, T value)
224224

225225
UnmanagedSpanOwner<T> data = new UnmanagedSpanOwner<T>(count);
226226

227-
foreach (ref byte n in MemoryMarshal.AsBytes(data.Span))
227+
foreach (ref byte n in MemoryMarshal.AsBytes(data.GetSpan()))
228228
{
229229
n = (byte)random.Next(0, byte.MaxValue);
230230
}
231231

232232
// Fill at least 20% of the items with a matching value
233233
int minimum = count / 20;
234234

235-
Span<T> span = data.Span;
235+
Span<T> span = data.GetSpan();
236236

237237
for (int i = 0; i < minimum; i++)
238238
{
@@ -255,7 +255,7 @@ private static UnmanagedSpanOwner<T> CreateFilledData<T>(int count, T value)
255255
{
256256
UnmanagedSpanOwner<T> data = new UnmanagedSpanOwner<T>(count);
257257

258-
data.Span.Fill(value);
258+
data.GetSpan().Fill(value);
259259

260260
return data;
261261
}

UnitTests/UnitTests.HighPerformance.Shared/Helpers/Test_HashCode{T}.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ private static void TestForType<T>()
102102
{
103103
using UnmanagedSpanOwner<T> data = CreateRandomData<T>(count);
104104

105-
int hash1 = HashCode<T>.Combine(data.Span);
106-
int hash2 = HashCode<T>.Combine(data.Span);
105+
int hash1 = HashCode<T>.Combine(data.GetSpan());
106+
int hash2 = HashCode<T>.Combine(data.GetSpan());
107107

108108
Assert.AreEqual(hash1, hash2, $"Failed {typeof(T)} test with count {count}: got {hash1} and then {hash2}");
109109
}
@@ -123,7 +123,7 @@ private static UnmanagedSpanOwner<T> CreateRandomData<T>(int count)
123123

124124
UnmanagedSpanOwner<T> data = new UnmanagedSpanOwner<T>(count);
125125

126-
foreach (ref byte n in MemoryMarshal.AsBytes(data.Span))
126+
foreach (ref byte n in MemoryMarshal.AsBytes(data.GetSpan()))
127127
{
128128
n = (byte)random.Next(0, byte.MaxValue);
129129
}

UnitTests/UnitTests.HighPerformance.Shared/Helpers/Test_ParallelHelper.For.cs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.Toolkit.HighPerformance.Extensions;
77
using Microsoft.Toolkit.HighPerformance.Helpers;
88
using Microsoft.VisualStudio.TestTools.UnitTesting;
9+
using UnitTests.HighPerformance.Shared.Buffers.Internals;
910

1011
namespace UnitTests.HighPerformance.Helpers
1112
{
@@ -19,15 +20,17 @@ public partial class Test_ParallelHelper
1920

2021
[TestCategory("ParallelHelper")]
2122
[TestMethod]
22-
public void Test_ParallelHelper_ForWithIndices()
23+
public unsafe void Test_ParallelHelper_ForWithIndices()
2324
{
2425
foreach (int count in TestForCounts)
2526
{
26-
int[] data = new int[count];
27+
using UnmanagedSpanOwner<int> data = new UnmanagedSpanOwner<int>(count);
2728

28-
ParallelHelper.For(0, data.Length, new Assigner(data));
29+
data.GetSpan().Clear();
2930

30-
foreach (var item in data.Enumerate())
31+
ParallelHelper.For(0, data.Length, new Assigner(data.Length, data.Ptr));
32+
33+
foreach (var item in data.GetSpan().Enumerate())
3134
{
3235
if (item.Index != item.Value)
3336
{
@@ -56,15 +59,17 @@ public void Test_ParallelHelper_ForInvalidRange_RangeAll()
5659

5760
[TestCategory("ParallelHelper")]
5861
[TestMethod]
59-
public void Test_ParallelHelper_ForWithRanges()
62+
public unsafe void Test_ParallelHelper_ForWithRanges()
6063
{
6164
foreach (int count in TestForCounts)
6265
{
63-
int[] data = new int[count];
66+
using UnmanagedSpanOwner<int> data = new UnmanagedSpanOwner<int>(count);
67+
68+
data.GetSpan().Clear();
6469

65-
ParallelHelper.For(..data.Length, new Assigner(data));
70+
ParallelHelper.For(..data.Length, new Assigner(data.Length, data.Ptr));
6671

67-
foreach (var item in data.Enumerate())
72+
foreach (var item in data.GetSpan().Enumerate())
6873
{
6974
if (item.Index != item.Value)
7075
{
@@ -78,21 +83,31 @@ public void Test_ParallelHelper_ForWithRanges()
7883
/// <summary>
7984
/// A type implementing <see cref="IAction"/> to initialize an array
8085
/// </summary>
81-
private readonly struct Assigner : IAction
86+
private readonly unsafe struct Assigner : IAction
8287
{
83-
private readonly int[] array;
88+
private readonly int length;
89+
private readonly int* ptr;
8490

85-
public Assigner(int[] array) => this.array = array;
91+
public Assigner(int length, int* ptr)
92+
{
93+
this.length = length;
94+
this.ptr = ptr;
95+
}
8696

8797
/// <inheritdoc/>
8898
public void Invoke(int i)
8999
{
90-
if (this.array[i] != 0)
100+
if ((uint)i >= (uint)this.length)
101+
{
102+
throw new IndexOutOfRangeException($"The target position was out of range, was {i} and should've been in [0, {this.length})");
103+
}
104+
105+
if (this.ptr[i] != 0)
91106
{
92-
throw new InvalidOperationException($"Invalid target position {i}, was {this.array[i]} instead of 0");
107+
throw new InvalidOperationException($"Invalid target position {i}, was {this.ptr[i]} instead of 0");
93108
}
94109

95-
this.array[i] = i;
110+
this.ptr[i] = i;
96111
}
97112
}
98113
}

UnitTests/UnitTests.HighPerformance.Shared/Helpers/Test_ParallelHelper.For2D.cs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Drawing;
77
using Microsoft.Toolkit.HighPerformance.Helpers;
88
using Microsoft.VisualStudio.TestTools.UnitTesting;
9+
using UnitTests.HighPerformance.Shared.Buffers.Internals;
910

1011
namespace UnitTests.HighPerformance.Helpers
1112
{
@@ -27,21 +28,23 @@ public partial class Test_ParallelHelper
2728

2829
[TestCategory("ParallelHelper")]
2930
[TestMethod]
30-
public void Test_ParallelHelper_For2DWithIndices()
31+
public unsafe void Test_ParallelHelper_For2DWithIndices()
3132
{
3233
foreach (var size in TestFor2DSizes)
3334
{
34-
int[,] data = new int[size.Height, size.Width];
35+
using UnmanagedSpanOwner<int> data = new UnmanagedSpanOwner<int>(size.Height * size.Width);
3536

36-
ParallelHelper.For2D(0, size.Height, 0, size.Width, new Assigner2D(data));
37+
data.GetSpan().Clear();
38+
39+
ParallelHelper.For2D(0, size.Height, 0, size.Width, new Assigner2D(size.Height, size.Width, data.Ptr));
3740

3841
for (int i = 0; i < size.Height; i++)
3942
{
4043
for (int j = 0; j < size.Width; j++)
4144
{
42-
if (data[i, j] != unchecked(i * 397 ^ j))
45+
if (data.Ptr[(i * size.Width) + j] != unchecked(i * 397 ^ j))
4346
{
44-
Assert.Fail($"Invalid item at position [{i},{j}], value was {data[i, j]} instead of {unchecked(i * 397 ^ j)}");
47+
Assert.Fail($"Invalid item at position [{i},{j}], value was {data.Ptr[(i * size.Width) + j]} instead of {unchecked(i * 397 ^ j)}");
4548
}
4649
}
4750
}
@@ -67,21 +70,23 @@ public void Test_ParallelHelper_For2DInvalidRange_RangeAll()
6770

6871
[TestCategory("ParallelHelper")]
6972
[TestMethod]
70-
public void Test_ParallelHelper_For2DWithRanges()
73+
public unsafe void Test_ParallelHelper_For2DWithRanges()
7174
{
7275
foreach (var size in TestFor2DSizes)
7376
{
74-
int[,] data = new int[size.Height, size.Width];
77+
using UnmanagedSpanOwner<int> data = new UnmanagedSpanOwner<int>(size.Height * size.Width);
78+
79+
data.GetSpan().Clear();
7580

76-
ParallelHelper.For2D(..size.Height, ..size.Width, new Assigner2D(data));
81+
ParallelHelper.For2D(..size.Height, ..size.Width, new Assigner2D(size.Height, size.Width, data.Ptr));
7782

7883
for (int i = 0; i < size.Height; i++)
7984
{
8085
for (int j = 0; j < size.Width; j++)
8186
{
82-
if (data[i, j] != unchecked(i * 397 ^ j))
87+
if (data.Ptr[(i * size.Width) + j] != unchecked(i * 397 ^ j))
8388
{
84-
Assert.Fail($"Invalid item at position [{i},{j}], value was {data[i, j]} instead of {unchecked(i * 397 ^ j)}");
89+
Assert.Fail($"Invalid item at position [{i},{j}], value was {data.Ptr[(i * size.Width) + j]} instead of {unchecked(i * 397 ^ j)}");
8590
}
8691
}
8792
}
@@ -92,21 +97,34 @@ public void Test_ParallelHelper_For2DWithRanges()
9297
/// <summary>
9398
/// A type implementing <see cref="IAction"/> to initialize a 2D array
9499
/// </summary>
95-
private readonly struct Assigner2D : IAction2D
100+
private readonly unsafe struct Assigner2D : IAction2D
96101
{
97-
private readonly int[,] array;
102+
private readonly int height;
103+
private readonly int width;
104+
private readonly int* ptr;
98105

99-
public Assigner2D(int[,] array) => this.array = array;
106+
public Assigner2D(int height, int width, int* ptr)
107+
{
108+
this.height = height;
109+
this.width = width;
110+
this.ptr = ptr;
111+
}
100112

101113
/// <inheritdoc/>
102114
public void Invoke(int i, int j)
103115
{
104-
if (this.array[i, j] != 0)
116+
if ((uint)i >= (uint)this.height ||
117+
(uint)j >= (uint)this.width)
118+
{
119+
throw new IndexOutOfRangeException($"The target position was invalid, was [{i}, {j}], should've been in [0, {this.height}] and [0, {this.width}]");
120+
}
121+
122+
if (this.ptr[(i * this.width) + j] != 0)
105123
{
106-
throw new InvalidOperationException($"Invalid target position [{i},{j}], was {this.array[i, j]} instead of 0");
124+
throw new InvalidOperationException($"Invalid target position [{i},{j}], was {this.ptr[(i * this.width) + j]} instead of 0");
107125
}
108126

109-
this.array[i, j] = unchecked(i * 397 ^ j);
127+
this.ptr[(i * this.width) + j] = unchecked(i * 397 ^ j);
110128
}
111129
}
112130
}

0 commit comments

Comments
 (0)