Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8cb7f18
Enabled hack to use ByReference<T> from the BCL
Sergio0694 Jun 27, 2020
17eb20f
Tweaks to ByReference<T>, added XML comments
Sergio0694 Jun 27, 2020
f15ce9e
Added missing attribute property
Sergio0694 Jun 27, 2020
5969f63
Switched NullableReadOnlyRef<T> to ByReference<T>
Sergio0694 Jun 27, 2020
024dab2
Updated all ref-like types to use ByReference<T>
Sergio0694 Jun 27, 2020
224a23c
Removed private hasValue field in nullable refs
Sergio0694 Jun 27, 2020
428259b
Minor performance improvement in nullable ref types
Sergio0694 Jun 27, 2020
78a0266
Optimized enumerable Item types
Sergio0694 Jun 27, 2020
1c71228
Fixed the HasValue property for nullable ref-like types
Sergio0694 Jun 27, 2020
9cee377
Added DangerousGetReferenceAt<T> to ref-like types
Sergio0694 Jun 28, 2020
227203a
Added ReadOnlyRef<T>.DangerousGetReference<T>()
Sergio0694 Jun 28, 2020
73f7494
Added unit tests for new APIs
Sergio0694 Jun 28, 2020
a82b60c
Bug fixes in [ReadOnly]Ref<T>.DangerousGetReferenceAt<T>()
Sergio0694 Jun 28, 2020
adbb772
Added dummy ByReference<T> field
Sergio0694 Jun 28, 2020
ed01bc6
Minor codegen improvements
Sergio0694 Jun 30, 2020
ddf5230
Added missing explicit ref-like type conversions
Sergio0694 Jun 30, 2020
71bc879
Merge branch 'master' into feature/byreference-trick
Sergio0694 Jun 30, 2020
06d218b
Added void* constructors for [ReadOnly]Ref<T> types
Sergio0694 Jul 1, 2020
7b0f61c
Removed unnecessary Ref<T> constructor
Sergio0694 Jul 1, 2020
23696b4
Added unit tests for new void* constructors
Sergio0694 Jul 1, 2020
b888a55
Renamed incorrect unit tests for ReadOnlyRef<T>
Sergio0694 Jul 1, 2020
c7eb731
Fixed exception type for void* constructors
Sergio0694 Jul 2, 2020
c593144
ByReference<T> is now internal again (as by default)
Sergio0694 Jul 2, 2020
8e7118e
Added PrivateAssets="all" to System.Private.CoreLib reference
Sergio0694 Jul 2, 2020
71d41cb
Disabled NuGet packaging for CoreLib project
Sergio0694 Jul 2, 2020
2abe9c1
Added blank Directory.Build.* files for CoreLib assembly
Sergio0694 Jul 2, 2020
e451e17
Merge branch 'master' into feature/byreference-trick
Sergio0694 Jul 10, 2020
3fbe7eb
Removed movsxd from [ReadOnly]Ref<T> indexers
Sergio0694 Jul 13, 2020
7803750
Merge branch 'master' into feature/byreference-trick
Sergio0694 Jul 13, 2020
c32693b
Fixed typo, added notes to ByReference<T>
Sergio0694 Jul 14, 2020
94efa11
Merge branch 'master' into feature/byreference-trick
Sergio0694 Jul 21, 2020
1625615
Revert "Optimized enumerable Item types"
Sergio0694 Jul 27, 2020
89e2e83
Added more comments for System.Private.CoreLib
Sergio0694 Jul 31, 2020
ef197ba
Merge branch 'master' into feature/byreference-trick
Sergio0694 Jul 31, 2020
7975942
Merge branch 'master' into feature/byreference-trick
Sergio0694 Aug 1, 2020
348b2ad
Merge branch 'master' into feature/byreference-trick
Sergio0694 Aug 12, 2020
333d447
Added [ReadOnly]Ref<T> indexers, code refactoring
Sergio0694 Aug 23, 2020
6262a73
Merge branch 'master' into feature/byreference-trick
Sergio0694 Aug 24, 2020
65cf1f0
Merge branch 'master' into feature/byreference-trick
Sergio0694 Sep 11, 2020
dc0d695
Merge branch 'master' into feature/byreference-trick
michael-hawker Oct 8, 2020
c7cb273
Added comments to unit tests
Sergio0694 Oct 30, 2020
cb489ab
Merge branch 'master' into feature/byreference-trick
michael-hawker Nov 1, 2020
1e9bc48
Merge branch 'master' into feature/byreference-trick
Sergio0694 Nov 6, 2020
1d1bb80
Refactored IntPtr indexer to nint
Sergio0694 Nov 6, 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,31 @@
// 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 NETCORE_RUNTIME

namespace System.Runtime.CompilerServices
{
/// <summary>
/// A special attribute recognized by CoreCLR that allows to ignore visibility checks for internal APIs.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="IgnoresAccessChecksToAttribute"/> class.
/// </summary>
/// <param name="assemblyName">The assembly name to use.</param>
public IgnoresAccessChecksToAttribute(string assemblyName)
{
AssemblyName = assemblyName;
}

/// <summary>
/// Gets the assembly name to use.
/// </summary>
public string AssemblyName { get; }
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@
</PropertyGroup>
</When>
<When Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
<ItemGroup>

<!-- This is the fake System.Private.CoreLib assembly we need to be able to reference and use the
internal ByReference<T> type available on .NET Core 2.1+. The Private="false" property prevents
the resulting assembly from being copied to the output directory: this is what causes the runtime
to just use the real System.Private.CoreLib during execution (this project acts more like a proxy).
The PrivateAssets="all" property is used to hide this reference from NuGet, so that users adding a
reference to Microsoft.Toolkit.HighPerformance won't see the System.Private.CoreLib dependency. -->
<ProjectReference Include="..\System.Private.CoreLib\System.Private.CoreLib.csproj" Private="false" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>

<!-- NETCORE_RUNTIME: to avoid issues with APIs that assume a specific memory layout, we define a
Expand All @@ -91,6 +101,9 @@
<ItemGroup>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\System.Private.CoreLib\System.Private.CoreLib.csproj" Private="false" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
<DefineConstants>SPAN_RUNTIME_SUPPORT;NETCORE_RUNTIME</DefineConstants>
</PropertyGroup>
Expand Down
52 changes: 52 additions & 0 deletions Microsoft.Toolkit.HighPerformance/NullableReadOnlyRef{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

using System;
using System.Runtime.CompilerServices;
#if !NETCORE_RUNTIME
using System.Runtime.InteropServices;
#endif

namespace Microsoft.Toolkit.HighPerformance
{
Expand All @@ -16,6 +18,32 @@ namespace Microsoft.Toolkit.HighPerformance
/// <typeparam name="T">The type of value to reference.</typeparam>
public readonly ref struct NullableReadOnlyRef<T>
{
#if NETCORE_RUNTIME
/// <summary>
/// The <see cref="ByReference{T}"/> instance holding the current reference.
/// </summary>
private readonly ByReference<T> byReference;

/// <summary>
/// Initializes a new instance of the <see cref="NullableReadOnlyRef{T}"/> struct.
/// </summary>
/// <param name="value">The readonly reference to the target <typeparamref name="T"/> value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NullableReadOnlyRef(in T value)
{
this.byReference = new ByReference<T>(ref Unsafe.AsRef(value));
}

/// <summary>
/// Initializes a new instance of the <see cref="NullableReadOnlyRef{T}"/> struct.
/// </summary>
/// <param name="byReference">The <see cref="ByReference{T}"/> instance holding the target reference.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private NullableReadOnlyRef(ByReference<T> byReference)
{
this.byReference = byReference;
}
#else
/// <summary>
/// The 1-length <see cref="ReadOnlySpan{T}"/> instance used to track the target <typeparamref name="T"/> value.
/// </summary>
Expand All @@ -42,6 +70,7 @@ private NullableReadOnlyRef(ReadOnlySpan<T> span)
{
this.span = span;
}
#endif

/// <summary>
/// Gets a <see cref="NullableReadOnlyRef{T}"/> instance representing a <see langword="null"/> reference.
Expand All @@ -60,10 +89,17 @@ public bool HasValue
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if NETCORE_RUNTIME
unsafe
{
return !Unsafe.AreSame(ref this.byReference.Value, ref Unsafe.AsRef<T>(null));
}
#else
// See comment in NullableRef<T> about this
byte length = unchecked((byte)this.span.Length);

return Unsafe.As<byte, bool>(ref length);
#endif
}
}

Expand All @@ -81,7 +117,11 @@ public ref readonly T Value
ThrowInvalidOperationException();
}

#if NETCORE_RUNTIME
return ref this.byReference.Value;
#else
return ref MemoryMarshal.GetReference(this.span);
#endif
}
}

Expand All @@ -92,7 +132,11 @@ public ref readonly T Value
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NullableReadOnlyRef<T>(Ref<T> reference)
{
#if NETCORE_RUNTIME
return new NullableReadOnlyRef<T>(reference.ByReference);
#else
return new NullableReadOnlyRef<T>(reference.Span);
#endif
}

/// <summary>
Expand All @@ -102,7 +146,11 @@ public static implicit operator NullableReadOnlyRef<T>(Ref<T> reference)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NullableReadOnlyRef<T>(ReadOnlyRef<T> reference)
{
#if NETCORE_RUNTIME
return new NullableReadOnlyRef<T>(reference.ByReference);
#else
return new NullableReadOnlyRef<T>(reference.Span);
#endif
}

/// <summary>
Expand All @@ -112,7 +160,11 @@ public static implicit operator NullableReadOnlyRef<T>(ReadOnlyRef<T> reference)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NullableReadOnlyRef<T>(NullableRef<T> reference)
{
#if NETCORE_RUNTIME
return new NullableReadOnlyRef<T>(reference.ByReference);
#else
return new NullableReadOnlyRef<T>(reference.Span);
#endif
}

/// <summary>
Expand Down
44 changes: 44 additions & 0 deletions Microsoft.Toolkit.HighPerformance/NullableRef{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

using System;
using System.Runtime.CompilerServices;
#if !NETCORE_RUNTIME
using System.Runtime.InteropServices;
#endif

namespace Microsoft.Toolkit.HighPerformance
{
Expand All @@ -16,6 +18,32 @@ namespace Microsoft.Toolkit.HighPerformance
/// <typeparam name="T">The type of value to reference.</typeparam>
public readonly ref struct NullableRef<T>
{
#if NETCORE_RUNTIME
/// <summary>
/// The <see cref="ByReference{T}"/> instance holding the current reference.
/// </summary>
internal readonly ByReference<T> ByReference;

/// <summary>
/// Initializes a new instance of the <see cref="NullableRef{T}"/> struct.
/// </summary>
/// <param name="value">The readonly reference to the target <typeparamref name="T"/> value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NullableRef(ref T value)
{
ByReference = new ByReference<T>(ref value);
}

/// <summary>
/// Initializes a new instance of the <see cref="NullableRef{T}"/> struct.
/// </summary>
/// <param name="byReference">The <see cref="ByReference{T}"/> instance holding the target reference.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private NullableRef(ByReference<T> byReference)
{
ByReference = byReference;
}
#else
/// <summary>
/// The 1-length <see cref="Span{T}"/> instance used to track the target <typeparamref name="T"/> value.
/// </summary>
Expand All @@ -40,6 +68,7 @@ private NullableRef(Span<T> span)
{
Span = span;
}
#endif

/// <summary>
/// Gets a <see cref="NullableRef{T}"/> instance representing a <see langword="null"/> reference.
Expand All @@ -58,6 +87,12 @@ public bool HasValue
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if NETCORE_RUNTIME
unsafe
{
return !Unsafe.AreSame(ref ByReference.Value, ref Unsafe.AsRef<T>(null));
}
#else
// We know that the span will always have a length of either
// 1 or 0, so instead of using a cmp instruction and setting the
// zero flag to produce our boolean value, we can just cast
Expand All @@ -68,6 +103,7 @@ public bool HasValue
byte length = unchecked((byte)Span.Length);

return Unsafe.As<byte, bool>(ref length);
#endif
}
}

Expand All @@ -85,7 +121,11 @@ public ref T Value
ThrowInvalidOperationException();
}

#if NETCORE_RUNTIME
return ref ByReference.Value;
#else
return ref MemoryMarshal.GetReference(Span);
#endif
}
}

Expand All @@ -96,7 +136,11 @@ public ref T Value
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NullableRef<T>(Ref<T> reference)
{
#if NETCORE_RUNTIME
return new NullableRef<T>(reference.ByReference);
#else
return new NullableRef<T>(reference.Span);
#endif
}

/// <summary>
Expand Down
20 changes: 20 additions & 0 deletions Microsoft.Toolkit.HighPerformance/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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 NETCORE_RUNTIME

using System.Runtime.CompilerServices;

// This is needed so that the runtime will actually let us access the internal
// ByReference<T> type exposed through the fake System.Private.CoreLib assembly
// that is referenced by this project. The reason for this is that while the proxy
// type we declared belongs to a project we have internals access to, the real
// ByReference<T> from the actual CoreLib assembly does not, so the runtime would
// normally fail to load the type if this attribute wasn't present.
// Note that while this attribute is internally recognized by the runtime, we still
// need the fake CoreLib assembly to be able to use the type when building from source,
// as Roslyn itself does not actually recognize and/or respect it.
[assembly: IgnoresAccessChecksTo("System.Private.CoreLib")]

#endif
Loading