Skip to content
This repository was archived by the owner on Feb 25, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c6d95bd
Initial .NET 5 support to HighPerformance package
Sergio0694 Jun 15, 2020
2815e8f
Added ListExtensions.AsSpan on .NET 5
Sergio0694 Jun 20, 2020
3940fe0
Updated .NET 5 compiler directives
Sergio0694 Oct 9, 2020
bb52951
Fixed compiler switches for .NET Core 3.1 and .NET 5
Sergio0694 Oct 9, 2020
2e53d7c
Added SkipLocalsInit attribute
Sergio0694 Oct 20, 2020
7e48bc1
Enabled [SkipLocalsInit] for all HighPerformance targets
Sergio0694 Oct 23, 2020
d29381f
Fixed visibility of local SkipLocalsInitAttribute copy
Sergio0694 Oct 24, 2020
aabf73d
Added [SkipLocalsInit] to Microsoft.Toolkit package
Sergio0694 Oct 24, 2020
d410686
Added missing nullability annotation
Sergio0694 Nov 5, 2020
e79f020
Minor code style tweak
Sergio0694 Nov 5, 2020
631d8f7
Fixed 2D array indexing on .NET 5 Mono
Sergio0694 Nov 5, 2020
5739c7f
Merge branch 'master' into feature/net-5-target
Sergio0694 Nov 10, 2020
35267b1
Merge branch 'master' into feature/net-5-target
Sergio0694 Nov 10, 2020
d7de21b
Merge branch 'master' into feature/net-5-target
Kyaa-dost Nov 11, 2020
f2cb8a6
Merge branch 'master' into feature/net-5-target
Sergio0694 Nov 20, 2020
20f211a
Fixed missing compiler directive in unit tests
Sergio0694 Nov 20, 2020
50bd227
Fixed missing NET 5 path in BitOperations
Sergio0694 Nov 20, 2020
d3f9e56
Merge branch 'master' into feature/net-5-target
Sergio0694 Nov 21, 2020
ef08f54
Removed unnecessary using directive
Sergio0694 Nov 21, 2020
81a2380
Fixed incorrect visibility of some throw helpers
Sergio0694 Nov 21, 2020
05b08a9
Suppressed compiler warnings
Sergio0694 Nov 21, 2020
c177ecf
Minor code style tweaks
Sergio0694 Nov 21, 2020
0ebe685
Minor code quality improvements
Sergio0694 Nov 21, 2020
e6f88c8
Enabled missing fast code paths on .NET 5
Sergio0694 Nov 21, 2020
bb46627
Aligned ThrowHelper nullability annotations to .NET 5
Sergio0694 Nov 22, 2020
7312d78
Added .NET 5 target to Microsoft.Toolkit package
Sergio0694 Nov 22, 2020
a178cc1
Minor code tweak to ToHexString
Sergio0694 Nov 22, 2020
e3d533a
Minor code style tweaks
Sergio0694 Nov 22, 2020
12f66e9
Fixed nullability annotations in Microsoft.Toolkit
Sergio0694 Nov 22, 2020
cd3c560
Removed unused attribute
Sergio0694 Nov 22, 2020
5534643
Removed unnecessary nullability attribute
Sergio0694 Nov 22, 2020
3f752a8
Minor code style tweaks
Sergio0694 Nov 22, 2020
26f4907
Code refactoring, improved nullability annotations
Sergio0694 Nov 22, 2020
a3b0ba6
Removed nullability warning suppressions
Sergio0694 Nov 22, 2020
c85af73
Fixed a small bug, minor code tweaks
Sergio0694 Nov 23, 2020
9322de6
Merge branch 'master' into feature/net-5-target
Rosuavio Nov 24, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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.

#if !NET5_0

namespace System.Runtime.CompilerServices
{
/// <summary>
/// Used to indicate to the compiler that the <c>.locals init</c> flag should not be set in method headers.
/// </summary>
/// <remarks>Internal copy of the .NET 5 attribute.</remarks>
[AttributeUsage(
AttributeTargets.Module |
AttributeTargets.Class |
AttributeTargets.Struct |
AttributeTargets.Interface |
AttributeTargets.Constructor |
AttributeTargets.Method |
AttributeTargets.Property |
AttributeTargets.Event,
Inherited = false)]
internal sealed class SkipLocalsInitAttribute : Attribute
{
}
}

#endif
4 changes: 2 additions & 2 deletions Microsoft.Toolkit.HighPerformance/Buffers/MemoryOwner{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
#if NETCORE_RUNTIME
#if NETCORE_RUNTIME || NET5_0
using System.Runtime.InteropServices;
#endif
using Microsoft.Toolkit.HighPerformance.Buffers.Views;
Expand Down Expand Up @@ -183,7 +183,7 @@ public Span<T> Span
ThrowObjectDisposedException();
}

#if NETCORE_RUNTIME
#if NETCORE_RUNTIME || NET5_0
ref T r0 = ref array!.DangerousGetReferenceAt(this.start);

// On .NET Core runtimes, we can manually create a span from the starting reference to
Expand Down
4 changes: 2 additions & 2 deletions Microsoft.Toolkit.HighPerformance/Buffers/SpanOwner{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
#if NETCORE_RUNTIME
#if NETCORE_RUNTIME || NET5_0
using System.Runtime.InteropServices;
#endif
using Microsoft.Toolkit.HighPerformance.Buffers.Views;
Expand Down Expand Up @@ -148,7 +148,7 @@ public Span<T> Span
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if NETCORE_RUNTIME
#if NETCORE_RUNTIME || NET5_0
ref T r0 = ref array!.DangerousGetReference();

return MemoryMarshal.CreateSpan(ref r0, this.length);
Expand Down
2 changes: 1 addition & 1 deletion Microsoft.Toolkit.HighPerformance/Buffers/StringPool.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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.

Expand Down
15 changes: 11 additions & 4 deletions Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.1D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
#if NETCORE_RUNTIME
#if NETCORE_RUNTIME || NET5_0
using System.Runtime.InteropServices;
#endif
using Microsoft.Toolkit.HighPerformance.Enumerables;
Expand All @@ -30,7 +30,9 @@ public static partial class ArrayExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T DangerousGetReference<T>(this T[] array)
{
#if NETCORE_RUNTIME
#if NET5_0
return ref MemoryMarshal.GetArrayDataReference(array);
#elif NETCORE_RUNTIME
var arrayData = Unsafe.As<RawArrayData>(array)!;
ref T r0 = ref Unsafe.As<byte, T>(ref arrayData.Data);

Expand All @@ -54,7 +56,12 @@ public static ref T DangerousGetReference<T>(this T[] array)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T DangerousGetReferenceAt<T>(this T[] array, int i)
{
#if NETCORE_RUNTIME
#if NET5_0
ref T r0 = ref MemoryMarshal.GetArrayDataReference(array);
ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)i);

return ref ri;
#elif NETCORE_RUNTIME
var arrayData = Unsafe.As<RawArrayData>(array)!;
ref T r0 = ref Unsafe.As<byte, T>(ref arrayData.Data);
ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)i);
Expand Down Expand Up @@ -203,7 +210,7 @@ public static bool IsCovariant<T>(this T[] array)
/// <summary>
/// Throws an <see cref="OverflowException"/> when the "column" parameter is invalid.
/// </summary>
public static void ThrowOverflowException()
private static void ThrowOverflowException()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this public in 6.1? This is just an internal helper, eh?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thankfully not 🤣
This is just an internal helper that I added in #3353, but that accidentally left out as public there. I think it was because this was initially in an internal throw helper class (so the method was public), then I moved it here and forgot to change the accessibility modifier. Really glad I spotted it in time for 7.0 😅

{
throw new OverflowException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,15 +451,15 @@ private static void ThrowArrayTypeMismatchException()
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the "row" parameter is invalid.
/// </summary>
public static void ThrowArgumentOutOfRangeExceptionForRow()
private static void ThrowArgumentOutOfRangeExceptionForRow()
{
throw new ArgumentOutOfRangeException("row");
}

/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the "column" parameter is invalid.
/// </summary>
public static void ThrowArgumentOutOfRangeExceptionForColumn()
private static void ThrowArgumentOutOfRangeExceptionForColumn()
{
throw new ArgumentOutOfRangeException("column");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ public static bool IsCovariant<T>(this T[,,] array)
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the "depth" parameter is invalid.
/// </summary>
public static void ThrowArgumentOutOfRangeExceptionForDepth()
private static void ThrowArgumentOutOfRangeExceptionForDepth()
{
throw new ArgumentOutOfRangeException("depth");
}
Expand Down
16 changes: 8 additions & 8 deletions Microsoft.Toolkit.HighPerformance/Extensions/BoolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ public static class BoolExtensions
/// <remarks>This method does not contain branching instructions.</remarks>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte ToByte(this bool flag)
public static unsafe byte ToByte(this bool flag)
{
return Unsafe.As<bool, byte>(ref flag);
return *(byte*)&flag;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New optimization from .NET 5 compiler???

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahahah nope, just good old C# trickery 😄
This was actually a suggestion from Tanner, the idea here is that the two versions do exactly the same anyway, but using raw pointers means the GC doesn't have to also track the additional metadata to ensure that if a GC collection happens while this method is running, the locals are properly handled. In our case that's not needed since the target values are already pinned (they're just local arguments passed by copy). So using raw pointers will produce the same exact codegen, but with some theoretical minor indirect benefits on top of that 👍

}

/// <summary>
Expand All @@ -35,9 +35,9 @@ public static byte ToByte(this bool flag)
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Obsolete("Use ToByte instead.")]
public static int ToInt(this bool flag)
public static unsafe int ToInt(this bool flag)
{
return Unsafe.As<bool, byte>(ref flag);
return *(byte*)&flag;
}

/// <summary>
Expand All @@ -56,9 +56,9 @@ public static int ToInt(this bool flag)
/// </remarks>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToBitwiseMask32(this bool flag)
public static unsafe int ToBitwiseMask32(this bool flag)
{
byte rangeFlag = Unsafe.As<bool, byte>(ref flag);
byte rangeFlag = *(byte*)&flag;
int
negativeFlag = rangeFlag - 1,
mask = ~negativeFlag;
Expand All @@ -75,9 +75,9 @@ public static int ToBitwiseMask32(this bool flag)
/// <remarks>This method does not contain branching instructions. See additional note in <see cref="ToBitwiseMask32"/>.</remarks>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long ToBitwiseMask64(this bool flag)
public static unsafe long ToBitwiseMask64(this bool flag)
{
byte rangeFlag = Unsafe.As<bool, byte>(ref flag);
byte rangeFlag = *(byte*)&flag;
long
negativeFlag = (long)rangeFlag - 1,
mask = ~negativeFlag;
Expand Down
42 changes: 42 additions & 0 deletions Microsoft.Toolkit.HighPerformance/Extensions/ListExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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.

#if NET5_0

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Microsoft.Toolkit.HighPerformance.Extensions
{
/// <summary>
/// Helpers for working with the <see cref="List{T}"/> type.
/// </summary>
public static class ListExtensions
{
/// <summary>
/// Creates a new <see cref="Span{T}"/> over an input <see cref="List{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of elements in the input <see cref="List{T}"/> instance.</typeparam>
/// <param name="list">The input <see cref="List{T}"/> instance.</param>
/// <returns>A <see cref="Span{T}"/> instance with the values of <paramref name="list"/>.</returns>
/// <remarks>
/// Note that the returned <see cref="Span{T}"/> is only guaranteed to be valid as long as the items within
/// <paramref name="list"/> are not modified. Doing so might cause the <see cref="List{T}"/> to swap its
/// internal buffer, causing the returned <see cref="Span{T}"/> to become out of date. That means that in this
/// scenario, the <see cref="Span{T}"/> would end up wrapping an array no longer in use. Always make sure to use
/// the returned <see cref="Span{T}"/> while the target <see cref="List{T}"/> is not modified.
/// </remarks>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> AsSpan<T>(this List<T>? list)
{
return CollectionsMarshal.AsSpan(list);
}
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public static ref T DangerousGetReferenceAt<T>(this ReadOnlySpan<T> span, nint i
/// </returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T DangerousGetLookupReferenceAt<T>(this ReadOnlySpan<T> span, int i)
public static unsafe ref readonly T DangerousGetLookupReferenceAt<T>(this ReadOnlySpan<T> span, int i)
{
// Check whether the input is in range by first casting both
// operands to uint and then comparing them, as this allows
Expand All @@ -156,7 +156,7 @@ public static ref readonly T DangerousGetLookupReferenceAt<T>(this ReadOnlySpan<
// bounds unless the input span was just empty, which for a
// lookup table can just be assumed to always be false.
bool isInRange = (uint)i < (uint)span.Length;
byte rangeFlag = Unsafe.As<bool, byte>(ref isInRange);
byte rangeFlag = *(byte*)&isInRange;
uint
negativeFlag = unchecked(rangeFlag - 1u),
mask = ~negativeFlag,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
#if !NETCOREAPP3_1
#if NETCOREAPP2_1 || NETSTANDARD
using System.Runtime.InteropServices;
#endif
using Microsoft.Toolkit.HighPerformance.Enumerables;
Expand All @@ -28,7 +28,7 @@ public static class StringExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref char DangerousGetReference(this string text)
{
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
return ref Unsafe.AsRef(text.GetPinnableReference());
#elif NETCOREAPP2_1
var stringData = Unsafe.As<RawStringData>(text)!;
Expand All @@ -50,7 +50,7 @@ public static ref char DangerousGetReference(this string text)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref char DangerousGetReferenceAt(this string text, int i)
{
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
ref char r0 = ref Unsafe.AsRef(text.GetPinnableReference());
#elif NETCOREAPP2_1
ref char r0 = ref Unsafe.As<RawStringData>(text)!.Data;
Expand Down
Loading