diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolExtensions.cs index 1531754432d..636df3e21e6 100644 --- a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolExtensions.cs +++ b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolExtensions.cs @@ -59,55 +59,5 @@ public static void Resize(this ArrayPool pool, ref T[]? array, int newSize array = newArray; } - - /// - /// Changes the number of elements of an instance to the specified new size. - /// - /// The type of items into the target buffer to resize. - /// The target instance to use to resize the buffer. - /// The rented instance to resize, or to create a new one. - /// The size of the new buffer. - /// Indicates whether the contents of the buffer should be cleared before reuse. - /// Thrown when is less than 0. - /// When this method returns, the caller must not use any references to the old buffer anymore. - public static void Resize(this MemoryPool pool, ref IMemoryOwner? memoryOwner, int newSize, bool clearBuffer = false) - { - if (newSize < 0) - { - throw new ArgumentOutOfRangeException(nameof(newSize), "The new size can't be less than 0"); - } - - // If the old memory is null, just create a new one with the requested size - if (memoryOwner is null) - { - memoryOwner = pool.Rent(newSize); - - return; - } - - // If the new size is the same as the current size, do nothing - var memory = memoryOwner.Memory; - if (memory.Length == newSize) - { - return; - } - - // Same behavior as the T[] resize extension - var newMemoryOwner = pool.Rent(newSize); - int itemsToCopy = Math.Min(memory.Length, newSize); - - memory.Slice(0, itemsToCopy).CopyTo(newMemoryOwner.Memory); - - // Clear the original buffer, if needed - if (clearBuffer) - { - memory.Span.Clear(); - } - - // There isn't a return API for the memory pool, so just invoke Dispose directly - memoryOwner.Dispose(); - - memoryOwner = newMemoryOwner; - } } } diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/HashCode{T}.cs b/Microsoft.Toolkit.HighPerformance/Helpers/HashCode{T}.cs similarity index 100% rename from Microsoft.Toolkit.HighPerformance/Extensions/HashCode{T}.cs rename to Microsoft.Toolkit.HighPerformance/Helpers/HashCode{T}.cs diff --git a/Microsoft.Toolkit.HighPerformance/Microsoft.Toolkit.HighPerformance.csproj b/Microsoft.Toolkit.HighPerformance/Microsoft.Toolkit.HighPerformance.csproj index ff8803efead..84afa766d87 100644 --- a/Microsoft.Toolkit.HighPerformance/Microsoft.Toolkit.HighPerformance.csproj +++ b/Microsoft.Toolkit.HighPerformance/Microsoft.Toolkit.HighPerformance.csproj @@ -12,8 +12,7 @@ - - + diff --git a/Microsoft.Toolkit.HighPerformance/ReadOnlyByReference{T}.cs b/Microsoft.Toolkit.HighPerformance/ReadOnlyByReference{T}.cs index 1a60eedade0..d1b7e130b23 100644 --- a/Microsoft.Toolkit.HighPerformance/ReadOnlyByReference{T}.cs +++ b/Microsoft.Toolkit.HighPerformance/ReadOnlyByReference{T}.cs @@ -39,7 +39,7 @@ public readonly ref struct ReadOnlyByReference /// The target offset within for the target reference. /// The parameter is not validated, and it's responsability of the caller to ensure it's valid. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlyByReference(object owner, int offset) + private ReadOnlyByReference(object owner, int offset) { this.owner = owner; this.offset = offset; diff --git a/UnitTests/UnitTests.HighPerformance/Extensions/Test_ArrayExtensions.cs b/UnitTests/UnitTests.HighPerformance/Extensions/Test_ArrayExtensions.cs new file mode 100644 index 00000000000..546f4d2d7aa --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance/Extensions/Test_ArrayExtensions.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using Microsoft.Toolkit.HighPerformance.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace UnitTests.HighPerformance.Extensions +{ + [TestClass] + public class Test_ArrayExtensions + { + [TestCategory("ArrayExtensions")] + [TestMethod] + public void Test_ArrayExtensions_DangerousGetReference() + { + string[] tokens = "aa,bb,cc,dd,ee,ff,gg,hh,ii".Split(','); + + ref string r0 = ref Unsafe.AsRef(tokens.DangerousGetReference()); + ref string r1 = ref Unsafe.AsRef(tokens[0]); + + Assert.IsTrue(Unsafe.AreSame(ref r0, ref r1)); + } + + [TestCategory("ArrayExtensions")] + [TestMethod] + public void Test_ArrayExtensions_DangerousGetReferenceAt_Zero() + { + string[] tokens = "aa,bb,cc,dd,ee,ff,gg,hh,ii".Split(','); + + ref string r0 = ref Unsafe.AsRef(tokens.DangerousGetReference()); + ref string r1 = ref Unsafe.AsRef(tokens.DangerousGetReferenceAt(0)); + + Assert.IsTrue(Unsafe.AreSame(ref r0, ref r1)); + } + + [TestCategory("ArrayExtensions")] + [TestMethod] + public void Test_ArrayExtensions_DangerousGetReferenceAt_Index() + { + string[] tokens = "aa,bb,cc,dd,ee,ff,gg,hh,ii".Split(','); + + ref string r0 = ref Unsafe.AsRef(tokens.DangerousGetReferenceAt(5)); + ref string r1 = ref Unsafe.AsRef(tokens[5]); + + Assert.IsTrue(Unsafe.AreSame(ref r0, ref r1)); + } + } +} diff --git a/UnitTests/UnitTests.HighPerformance/Extensions/Test_ArrayPoolExtensions.cs b/UnitTests/UnitTests.HighPerformance/Extensions/Test_ArrayPoolExtensions.cs new file mode 100644 index 00000000000..3a728313e1b --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance/Extensions/Test_ArrayPoolExtensions.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Buffers; +using System.Linq; +using Microsoft.Toolkit.HighPerformance.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace UnitTests.HighPerformance.Extensions +{ + [TestClass] + public class Test_ArrayPoolExtensions + { + [TestCategory("ArrayPoolExtensions")] + [TestMethod] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void Test_ArrayExtensions_InvalidSize() + { + int[] array = null; + + ArrayPool.Shared.Resize(ref array, -1); + } + + [TestCategory("ArrayPoolExtensions")] + [TestMethod] + public void Test_ArrayExtensions_NewArray() + { + int[] array = null; + + ArrayPool.Shared.Resize(ref array, 10); + + Assert.IsNotNull(array); + Assert.IsTrue(array.Length >= 10); + } + + [TestCategory("ArrayPoolExtensions")] + [TestMethod] + public void Test_ArrayExtensions_SameSize() + { + int[] array = ArrayPool.Shared.Rent(10); + int[] backup = array; + + ArrayPool.Shared.Resize(ref array, array.Length); + + Assert.AreSame(array, backup); + } + + [TestCategory("ArrayPoolExtensions")] + [TestMethod] + public void Test_ArrayExtensions_Expand() + { + int[] array = ArrayPool.Shared.Rent(16); + int[] backup = array; + + array.AsSpan().Fill(7); + + ArrayPool.Shared.Resize(ref array, 32); + + Assert.AreNotSame(array, backup); + Assert.IsTrue(array.Length >= 32); + Assert.IsTrue(array.AsSpan(0, 16).ToArray().All(i => i == 7)); + } + + [TestCategory("ArrayPoolExtensions")] + [TestMethod] + public void Test_ArrayExtensions_Shrink() + { + int[] array = ArrayPool.Shared.Rent(32); + int[] backup = array; + + array.AsSpan().Fill(7); + + ArrayPool.Shared.Resize(ref array, 16); + + Assert.AreNotSame(array, backup); + Assert.IsTrue(array.Length >= 16); + Assert.IsTrue(array.AsSpan(0, 16).ToArray().All(i => i == 7)); + } + + [TestCategory("ArrayPoolExtensions")] + [TestMethod] + public void Test_ArrayExtensions_Clear() + { + int[] array = ArrayPool.Shared.Rent(16); + int[] backup = array; + + array.AsSpan().Fill(7); + + ArrayPool.Shared.Resize(ref array, 0, true); + + Assert.AreNotSame(array, backup); + Assert.IsTrue(backup.AsSpan(0, 16).ToArray().All(i => i == 0)); + } + } +} diff --git a/UnitTests/UnitTests.HighPerformance/Extensions/Test_ReadOnlySpanExtensions.Count.cs b/UnitTests/UnitTests.HighPerformance/Extensions/Test_ReadOnlySpanExtensions.Count.cs new file mode 100644 index 00000000000..68d50be5833 --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance/Extensions/Test_ReadOnlySpanExtensions.Count.cs @@ -0,0 +1,171 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using Microsoft.Toolkit.HighPerformance.Extensions; + +namespace UnitTests.HighPerformance.Extensions +{ + public partial class Test_ReadOnlySpanExtensions + { + /// + /// Gets the list of counts to test the extension for + /// + private static ReadOnlySpan TestCounts => new[] { 0, 1, 7, 128, 255, 256, short.MaxValue, short.MaxValue + 1, 123_938, 1_678_922, 71_890_819 }; + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_RandomCount8() + { + TestForType((byte)123, CreateRandomData); + TestForType((sbyte)123, CreateRandomData); + TestForType(true, CreateRandomData); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_RandomCount16() + { + TestForType((ushort)4712, CreateRandomData); + TestForType((short)4712, CreateRandomData); + TestForType((char)4712, CreateRandomData); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_RandomCount32() + { + TestForType((int)37438941, CreateRandomData); + TestForType((uint)37438941, CreateRandomData); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_RandomCount64() + { + TestForType((long)47128480128401, CreateRandomData); + TestForType((ulong)47128480128401, CreateRandomData); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_FilledCount8() + { + TestForType((byte)123, count => CreateFilledData(count, (byte)123)); + TestForType((sbyte)123, count => CreateFilledData(count, (sbyte)123)); + TestForType(true, count => CreateFilledData(count, true)); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_FilledCount16() + { + TestForType((ushort)4712, count => CreateFilledData(count, (ushort)4712)); + TestForType((short)4712, count => CreateFilledData(count, (short)4712)); + TestForType((char)4712, count => CreateFilledData(count, (char)4712)); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_FilledCount32() + { + TestForType((int)37438941, count => CreateFilledData(count, (int)37438941)); + TestForType((uint)37438941, count => CreateFilledData(count, (uint)37438941)); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_FilledCount64() + { + TestForType((long)47128480128401, count => CreateFilledData(count, (long)47128480128401)); + TestForType((ulong)47128480128401, count => CreateFilledData(count, (ulong)47128480128401)); + } + + /// + /// Performs a test for a specified type. + /// + /// The type to test. + /// The target value to look for. + /// The function to use to create random data. + private static void TestForType(T value, Func provider) + where T : unmanaged, IEquatable + { + foreach (var count in TestCounts) + { + T[] data = provider(count); + + int result = data.Count(value); + int expected = CountWithForeach(data, value); + + Assert.AreEqual(result, expected, $"Failed {typeof(T)} test with count {count}: got {result} instead of {expected}"); + } + } + + /// + /// Counts the number of occurrences of a given value into a target instance. + /// + /// The input instance to read. + /// The value to look for. + /// The number of occurrences of in . + [Pure] + private static int CountWithForeach(ReadOnlySpan span, T value) + where T : IEquatable + { + int count = 0; + + foreach (var item in span) + { + if (item.Equals(value)) + { + count++; + } + } + + return count; + } + + /// + /// Creates a random array filled with random data. + /// + /// The type of items to put in the array. + /// The number of array items to create. + /// An array of random elements. + [Pure] + private static T[] CreateRandomData(int count) + where T : unmanaged + { + var random = new Random(count); + + T[] data = new T[count]; + + foreach (ref byte n in MemoryMarshal.AsBytes(data.AsSpan())) + { + n = (byte)random.Next(0, byte.MaxValue); + } + + return data; + } + + /// + /// Creates a array filled with a given value. + /// + /// The type of items to put in the array. + /// The number of array items to create. + /// The value to use to populate the array. + /// An array of elements. + [Pure] + private static T[] CreateFilledData(int count, T value) + where T : unmanaged + { + T[] data = new T[count]; + + data.AsSpan().Fill(value); + + return data; + } + } +} diff --git a/UnitTests/UnitTests.HighPerformance/Extensions/Test_ReadOnlySpanExtensions.cs b/UnitTests/UnitTests.HighPerformance/Extensions/Test_ReadOnlySpanExtensions.cs new file mode 100644 index 00000000000..8150ca089b2 --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance/Extensions/Test_ReadOnlySpanExtensions.cs @@ -0,0 +1,128 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Microsoft.Toolkit.HighPerformance.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace UnitTests.HighPerformance.Extensions +{ + [TestClass] + public partial class Test_ReadOnlySpanExtensions + { + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_DangerousGetReference() + { + ReadOnlySpan data = CreateRandomData(12).AsSpan(); + + ref int r0 = ref Unsafe.AsRef(data.DangerousGetReference()); + ref int r1 = ref Unsafe.AsRef(data[0]); + + Assert.IsTrue(Unsafe.AreSame(ref r0, ref r1)); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_DangerousGetReferenceAt_Zero() + { + ReadOnlySpan data = CreateRandomData(12).AsSpan(); + + ref int r0 = ref Unsafe.AsRef(data.DangerousGetReference()); + ref int r1 = ref Unsafe.AsRef(data.DangerousGetReferenceAt(0)); + + Assert.IsTrue(Unsafe.AreSame(ref r0, ref r1)); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_DangerousGetReferenceAt_Index() + { + ReadOnlySpan data = CreateRandomData(12).AsSpan(); + + ref int r0 = ref Unsafe.AsRef(data.DangerousGetReferenceAt(5)); + ref int r1 = ref Unsafe.AsRef(data[5]); + + Assert.IsTrue(Unsafe.AreSame(ref r0, ref r1)); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_Enumerate() + { + ReadOnlySpan data = CreateRandomData(12).AsSpan(); + + List<(int Index, int Value)> values = new List<(int, int)>(); + + foreach (var item in data.Enumerate()) + { + values.Add(item); + } + + Assert.AreEqual(values.Count, data.Length); + + for (int i = 0; i < data.Length; i++) + { + Assert.AreEqual(data[i], values[i].Value); + Assert.AreEqual(i, values[i].Index); + } + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_Tokenize_Empty() + { + string text = ""; + + var result = new List(); + + foreach (var token in text.AsSpan().Tokenize(',')) + { + result.Add(new string(token.ToArray())); + } + + var tokens = text.Split(','); + + CollectionAssert.AreEqual(result, tokens); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_Tokenize_Csv() + { + string text = "name,surname,city,year,profession,age"; + + var result = new List(); + + foreach (var token in text.AsSpan().Tokenize(',')) + { + result.Add(new string(token.ToArray())); + } + + var tokens = text.Split(','); + + CollectionAssert.AreEqual(result, tokens); + } + + [TestCategory("ReadOnlySpanExtensions")] + [TestMethod] + public void Test_ReadOnlySpanExtensions_Tokenize_CsvWithMissingValues() + { + string text = ",name,,city,,,profession,,age,,"; + + var result = new List(); + + foreach (var token in text.AsSpan().Tokenize(',')) + { + result.Add(new string(token.ToArray())); + } + + var tokens = text.Split(','); + + CollectionAssert.AreEqual(result, tokens); + } + } +} diff --git a/UnitTests/UnitTests.HighPerformance/Extensions/Test_SpinLockExtensions.cs b/UnitTests/UnitTests.HighPerformance/Extensions/Test_SpinLockExtensions.cs new file mode 100644 index 00000000000..c4e85e14f9e --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance/Extensions/Test_SpinLockExtensions.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Toolkit.HighPerformance.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace UnitTests.HighPerformance.Extensions +{ + [TestClass] + public class Test_SpinLockExtensions + { + [TestCategory("SpinLockExtensions")] + [TestMethod] + public unsafe void Test_ArrayExtensions_Pointer() + { + SpinLock spinLock = default; + SpinLock* p = &spinLock; + + int sum = 0; + + Parallel.For(0, 1000, i => + { + for (int j = 0; j < 10; j++) + { + using (SpinLockExtensions.Enter(p)) + { + sum++; + } + } + }); + + Assert.AreEqual(sum, 1000 * 10); + } + + [TestCategory("SpinLockExtensions")] + [TestMethod] + public void Test_ArrayExtensions_Ref() + { + var spinLockOwner = new SpinLockOwner(); + + int sum = 0; + + Parallel.For(0, 1000, i => + { + for (int j = 0; j < 10; j++) + { +#if NETCOREAPP2_1 + using (SpinLockExtensions.Enter(spinLockOwner, ref spinLockOwner.Lock)) +#else + using (spinLockOwner.Lock.Enter()) +#endif + { + sum++; + } + } + }); + + Assert.AreEqual(sum, 1000 * 10); + } + + /// + /// A dummy model that owns a object. + /// + private sealed class SpinLockOwner + { + public SpinLock Lock; + } + } +} diff --git a/UnitTests/UnitTests.HighPerformance/Extensions/Test_StringExtensions.cs b/UnitTests/UnitTests.HighPerformance/Extensions/Test_StringExtensions.cs new file mode 100644 index 00000000000..ca9ca945542 --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance/Extensions/Test_StringExtensions.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Toolkit.HighPerformance.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace UnitTests.HighPerformance.Extensions +{ + [TestClass] + public class Test_StringExtensions + { + [TestCategory("StringExtensions")] + [TestMethod] + public void Test_StringExtensions_DangerousGetReference() + { + string text = "Hello, world!"; + + ref char r0 = ref Unsafe.AsRef(text.DangerousGetReference()); + ref char r1 = ref Unsafe.AsRef(text.AsSpan()[0]); + + Assert.IsTrue(Unsafe.AreSame(ref r0, ref r1)); + } + + [TestCategory("StringExtensions")] + [TestMethod] + public void Test_StringExtensions_DangerousGetReferenceAt_Zero() + { + string text = "Hello, world!"; + + ref char r0 = ref Unsafe.AsRef(text.DangerousGetReference()); + ref char r1 = ref Unsafe.AsRef(text.DangerousGetReferenceAt(0)); + + Assert.IsTrue(Unsafe.AreSame(ref r0, ref r1)); + } + + [TestCategory("StringExtensions")] + [TestMethod] + public void Test_StringExtensions_DangerousGetReferenceAt_Index() + { + string text = "Hello, world!"; + + ref char r0 = ref Unsafe.AsRef(text.DangerousGetReferenceAt(5)); + ref char r1 = ref Unsafe.AsRef(text.AsSpan()[5]); + + Assert.IsTrue(Unsafe.AreSame(ref r0, ref r1)); + } + } +} diff --git a/UnitTests/UnitTests.HighPerformance/Test_ByReference{T}.cs b/UnitTests/UnitTests.HighPerformance/Test_ByReference{T}.cs new file mode 100644 index 00000000000..ac07c1e97f7 --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance/Test_ByReference{T}.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using Microsoft.Toolkit.HighPerformance; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace UnitTests.HighPerformance.Extensions +{ + [TestClass] + public class Test_ByReferenceOfT + { + [TestCategory("ByReferenceOfT")] + [TestMethod] +#if NETCOREAPP2_1 + public void Test_ByReferenceOfT_CreateByReferenceOfT() + { + var model = new FieldOwner { Value = 1 }; + var reference = new ByReference(model, ref model.Value); + + Assert.IsTrue(Unsafe.AreSame(ref model.Value, ref reference.Value)); + + reference.Value++; + + Assert.AreEqual(model.Value, 2); + } + + /// + /// A dummy model that owns an field. + /// + private sealed class FieldOwner + { + public int Value; + } +#else + public void Test_ByReferenceOfT_CreateByReferenceOfT() + { + int value = 1; + var reference = new ByReference(ref value); + + Assert.IsTrue(Unsafe.AreSame(ref value, ref reference.Value)); + + reference.Value++; + + Assert.AreEqual(value, 2); + + } +#endif + } +} diff --git a/UnitTests/UnitTests.HighPerformance/Test_ReadOnlyByReference{T}.cs b/UnitTests/UnitTests.HighPerformance/Test_ReadOnlyByReference{T}.cs new file mode 100644 index 00000000000..12ba74a1bc7 --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance/Test_ReadOnlyByReference{T}.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using Microsoft.Toolkit.HighPerformance; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace UnitTests.HighPerformance.Extensions +{ + [TestClass] + public class Test_ReadOnlyByReferenceT + { + [TestCategory("ReadOnlyByReferenceT")] + [TestMethod] +#if NETCOREAPP2_1 + public void Test_ByReferenceOfT_CreateByReferenceOfT() + { + var model = new FieldOwner { Value = 1 }; + var reference = new ReadOnlyByReference(model, model.Value); + + Assert.IsTrue(Unsafe.AreSame(ref model.Value, ref Unsafe.AsRef(reference.Value))); + } + + /// + /// A dummy model that owns an field. + /// + private sealed class FieldOwner + { + public readonly int Value; + } +#else + public void Test_ByReferenceOfT_CreateByReferenceOfT() + { + int value = 1; + var reference = new ReadOnlyByReference(value); + + Assert.IsTrue(Unsafe.AreSame(ref value, ref Unsafe.AsRef(reference.Value))); + + } +#endif + } +} diff --git a/UnitTests/UnitTests.HighPerformance/UnitTests.HighPerformance.csproj b/UnitTests/UnitTests.HighPerformance/UnitTests.HighPerformance.csproj new file mode 100644 index 00000000000..349a5aec026 --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance/UnitTests.HighPerformance.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.1;netcoreapp3.0 + 8.0 + true + false + + + + + + + + + + + + + + diff --git a/Windows Community Toolkit.sln b/Windows Community Toolkit.sln index a2921d10c45..83a2dcb125a 100644 --- a/Windows Community Toolkit.sln +++ b/Windows Community Toolkit.sln @@ -90,7 +90,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GazeInputTest", "GazeInputT EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Media", "Microsoft.Toolkit.Uwp.UI.Media\Microsoft.Toolkit.Uwp.UI.Media.csproj", "{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Toolkit.HighPerformance", "Microsoft.Toolkit.HighPerformance\Microsoft.Toolkit.HighPerformance.csproj", "{7E30D48C-4CD8-47BE-B557-10A20391DCC4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.HighPerformance", "Microsoft.Toolkit.HighPerformance\Microsoft.Toolkit.HighPerformance.csproj", "{7E30D48C-4CD8-47BE-B557-10A20391DCC4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.HighPerformance", "UnitTests\UnitTests.HighPerformance\UnitTests.HighPerformance.csproj", "{D9BDBC68-3D0A-47FC-9C88-0BF769101644}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -833,6 +835,36 @@ Global {7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Release|x64.Build.0 = Release|Any CPU {7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Release|x86.ActiveCfg = Release|Any CPU {7E30D48C-4CD8-47BE-B557-10A20391DCC4}.Release|x86.Build.0 = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|ARM.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|ARM64.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|x64.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|x64.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|x86.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Debug|x86.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|Any CPU.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|Any CPU.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|ARM.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|ARM.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|ARM64.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|ARM64.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|x64.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|x64.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|x86.ActiveCfg = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Native|x86.Build.0 = Debug|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|Any CPU.Build.0 = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|ARM.ActiveCfg = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|ARM.Build.0 = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|ARM64.ActiveCfg = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|ARM64.Build.0 = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|x64.ActiveCfg = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|x64.Build.0 = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|x86.ActiveCfg = Release|Any CPU + {D9BDBC68-3D0A-47FC-9C88-0BF769101644}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -856,6 +888,7 @@ Global {262BB7CE-EF42-4BF7-B90C-107E6CBB57FF} = {096ECFD7-7035-4487-9C87-81DCE9389620} {A122EA02-4DE7-413D-BFBF-AF7DFC668DD6} = {B30036C4-D514-4E5B-A323-587A061772CE} {75F9EE44-3EFA-47BC-AEDD-351B9834A0AF} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC} + {D9BDBC68-3D0A-47FC-9C88-0BF769101644} = {B30036C4-D514-4E5B-A323-587A061772CE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5403B0C4-F244-4F73-A35C-FE664D0F4345}