diff --git a/dependencies.props b/dependencies.props
index efe3a44cc..538d57466 100644
--- a/dependencies.props
+++ b/dependencies.props
@@ -3,7 +3,7 @@
5.0.0-preview.5.20230.9
1.0.0-alpha-28820-01
1.0.1-prerelease-00005
- 5.0.0-preview.2.20153.3
+ 5.0.0-preview.5.20263.12
4.7.0-preview6.19265.2
2.1.14
15.8.0
diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props
index b4b19ed24..fa1598df3 100644
--- a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props
+++ b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props
@@ -57,12 +57,12 @@ See the LICENSE file in the project root for more information.
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs b/src/Common/src/Interop/Interop.Calendar.cs
similarity index 98%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs
rename to src/Common/src/Interop/Interop.Calendar.cs
index 764bdaf85..0b9e2c283 100644
--- a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs
+++ b/src/Common/src/Interop/Interop.Calendar.cs
@@ -5,7 +5,6 @@
using System;
using System.Globalization;
using System.Runtime.InteropServices;
-using System.Text;
internal static partial class Interop
{
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Casing.cs b/src/Common/src/Interop/Interop.Casing.cs
similarity index 95%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Casing.cs
rename to src/Common/src/Interop/Interop.Casing.cs
index 503a864d6..499f9445d 100644
--- a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Casing.cs
+++ b/src/Common/src/Interop/Interop.Casing.cs
@@ -2,10 +2,7 @@
// 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.InteropServices;
-using System.Security;
-using System.Text;
internal static partial class Interop
{
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Collation.cs b/src/Common/src/Interop/Interop.Collation.cs
similarity index 98%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Collation.cs
rename to src/Common/src/Interop/Interop.Collation.cs
index 7d0175473..ece19be58 100644
--- a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Collation.cs
+++ b/src/Common/src/Interop/Interop.Collation.cs
@@ -5,7 +5,6 @@
using System;
using System.Globalization;
using System.Runtime.InteropServices;
-using System.Security;
internal static partial class Interop
{
@@ -24,7 +23,7 @@ internal static partial class Interop
internal static extern unsafe int IndexOf(IntPtr sortHandle, char* target, int cwTargetLength, char* pSource, int cwSourceLength, CompareOptions options, int* matchLengthPtr);
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_LastIndexOf")]
- internal static extern unsafe int LastIndexOf(IntPtr sortHandle, char* target, int cwTargetLength, char* pSource, int cwSourceLength, CompareOptions options);
+ internal static extern unsafe int LastIndexOf(IntPtr sortHandle, char* target, int cwTargetLength, char* pSource, int cwSourceLength, CompareOptions options, int* matchLengthPtr);
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOfOrdinalIgnoreCase")]
internal static extern unsafe int IndexOfOrdinalIgnoreCase(string target, int cwTargetLength, char* pSource, int cwSourceLength, bool findLast);
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.ICU.cs b/src/Common/src/Interop/Interop.ICU.cs
similarity index 92%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.ICU.cs
rename to src/Common/src/Interop/Interop.ICU.cs
index f24c26078..0dce07295 100644
--- a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.ICU.cs
+++ b/src/Common/src/Interop/Interop.ICU.cs
@@ -2,9 +2,7 @@
// 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.InteropServices;
-using System.Runtime.CompilerServices;
internal static partial class Interop
{
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Idna.cs b/src/Common/src/Interop/Interop.Idna.cs
similarity index 98%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Idna.cs
rename to src/Common/src/Interop/Interop.Idna.cs
index 89b6c3ceb..e9014da1e 100644
--- a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Idna.cs
+++ b/src/Common/src/Interop/Interop.Idna.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Interop.Libraries.cs b/src/Common/src/Interop/Interop.Libraries.cs
new file mode 100644
index 000000000..4fee38ad3
--- /dev/null
+++ b/src/Common/src/Interop/Interop.Libraries.cs
@@ -0,0 +1,11 @@
+// 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.
+
+internal static partial class Interop
+{
+ internal static partial class Libraries
+ {
+ internal const string GlobalizationNative = "libSystem.Globalization.Native";
+ }
+}
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Locale.cs b/src/Common/src/Interop/Interop.Locale.cs
similarity index 99%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Locale.cs
rename to src/Common/src/Interop/Interop.Locale.cs
index d9a300fd1..a6517185c 100644
--- a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Locale.cs
+++ b/src/Common/src/Interop/Interop.Locale.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs b/src/Common/src/Interop/Interop.Normalization.cs
similarity index 68%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs
rename to src/Common/src/Interop/Interop.Normalization.cs
index d442da0ea..f981eac64 100644
--- a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs
+++ b/src/Common/src/Interop/Interop.Normalization.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
using System.Text;
@@ -11,9 +10,9 @@ internal static partial class Interop
internal static partial class Globalization
{
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IsNormalized")]
- internal static extern int IsNormalized(NormalizationForm normalizationForm, string src, int srcLen);
+ internal static extern unsafe int IsNormalized(NormalizationForm normalizationForm, char* src, int srcLen);
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_NormalizeString")]
- internal static extern int NormalizeString(NormalizationForm normalizationForm, string src, int srcLen, [Out] char[] dstBuffer, int dstBufferCapacity);
+ internal static extern unsafe int NormalizeString(NormalizationForm normalizationForm, char* src, int srcLen, char* dstBuffer, int dstBufferCapacity);
}
}
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs b/src/Common/src/Interop/Interop.ResultCode.cs
similarity index 100%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs
rename to src/Common/src/Interop/Interop.ResultCode.cs
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs b/src/Common/src/Interop/Interop.TimeZoneInfo.cs
similarity index 98%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs
rename to src/Common/src/Interop/Interop.TimeZoneInfo.cs
index df488b6ff..f60b81f0d 100644
--- a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs
+++ b/src/Common/src/Interop/Interop.TimeZoneInfo.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
-using System.Text;
internal static partial class Interop
{
diff --git a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Utils.cs b/src/Common/src/Interop/Interop.Utils.cs
similarity index 97%
rename from src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Utils.cs
rename to src/Common/src/Interop/Interop.Utils.cs
index 627ab56c9..11534cbde 100644
--- a/src/Common/src/Interop/Unix/System.Globalization.Native/Interop.Utils.cs
+++ b/src/Common/src/Interop/Interop.Utils.cs
@@ -3,9 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Diagnostics;
using System.Buffers;
-using System.Text;
internal static partial class Interop
{
diff --git a/src/Common/src/Interop/Unix/Interop.Errors.cs b/src/Common/src/Interop/Unix/Interop.Errors.cs
index 49cc830e0..dad796280 100644
--- a/src/Common/src/Interop/Unix/Interop.Errors.cs
+++ b/src/Common/src/Interop/Unix/Interop.Errors.cs
@@ -115,7 +115,7 @@ internal static partial class Interop
// Represents a platform-agnostic Error and underlying platform-specific errno
internal struct ErrorInfo
{
- private Error _error;
+ private readonly Error _error;
private int _rawErrno;
internal ErrorInfo(int errno)
diff --git a/src/Common/src/Interop/Unix/Interop.Libraries.cs b/src/Common/src/Interop/Unix/Interop.Libraries.cs
index 10118ff30..f51194829 100644
--- a/src/Common/src/Interop/Unix/Interop.Libraries.cs
+++ b/src/Common/src/Interop/Unix/Interop.Libraries.cs
@@ -7,12 +7,12 @@ internal static partial class Interop
internal static partial class Libraries
{
// Shims
- internal const string SystemNative = "System.Native";
- internal const string GlobalizationNative = "System.Globalization.Native";
- internal const string NetSecurityNative = "System.Net.Security.Native";
- internal const string CryptoNative = "System.Security.Cryptography.Native.OpenSsl";
- internal const string CompressionNative = "System.IO.Compression.Native";
- internal const string IOPortsNative = "System.IO.Ports.Native";
+ internal const string SystemNative = "libSystem.Native";
+ internal const string NetSecurityNative = "libSystem.Net.Security.Native";
+ internal const string CryptoNative = "libSystem.Security.Cryptography.Native.OpenSsl";
+ internal const string CompressionNative = "libSystem.IO.Compression.Native";
+ internal const string IOPortsNative = "libSystem.IO.Ports.Native";
internal const string Libdl = "libdl";
+ internal const string HostPolicy = "libhostpolicy";
}
}
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.Access.cs b/src/Common/src/Interop/Unix/System.Native/Interop.Access.cs
index a723f572a..b9750fa10 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.Access.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.Access.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.ChDir.cs b/src/Common/src/Interop/Unix/System.Native/Interop.ChDir.cs
index 3c6699518..8979d10f4 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.ChDir.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.ChDir.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.EnumerateInterfaceAddresses.cs b/src/Common/src/Interop/Unix/System.Native/Interop.EnumerateInterfaceAddresses.cs
index 8e03a33b5..7ad346a9a 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.EnumerateInterfaceAddresses.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.EnumerateInterfaceAddresses.cs
@@ -51,8 +51,8 @@ internal static partial class Interop
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_EnumerateInterfaceAddresses")]
public static extern int EnumerateInterfaceAddresses(
IPv4AddressDiscoveredCallback ipv4Found,
- IPv6AddressDiscoveredCallback ipv6Found,
- LinkLayerAddressDiscoveredCallback linkLayerFound);
+ IPv6AddressDiscoveredCallback? ipv6Found,
+ LinkLayerAddressDiscoveredCallback? linkLayerFound);
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_EnumerateGatewayAddressesForInterface")]
public static extern int EnumerateGatewayAddressesForInterface(uint interfaceIndex, DnsAddessDiscoveredCallback onGatewayFound);
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.Fcntl.cs b/src/Common/src/Interop/Unix/System.Native/Interop.Fcntl.cs
index cec6451b5..0877ca707 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.Fcntl.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.Fcntl.cs
@@ -17,6 +17,9 @@ internal static partial class Interop
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FcntlSetIsNonBlocking", SetLastError=true)]
internal static extern int SetIsNonBlocking(SafeHandle fd, int isNonBlocking);
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FcntlGetIsNonBlocking", SetLastError = true)]
+ internal static extern int GetIsNonBlocking(SafeHandle fd, out bool isNonBlocking);
+
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FcntlSetFD", SetLastError=true)]
internal static extern int SetFD(SafeHandle fd, int flags);
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.GetCpuUtilization.cs b/src/Common/src/Interop/Unix/System.Native/Interop.GetCpuUtilization.cs
index a630a3e82..509fef827 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.GetCpuUtilization.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.GetCpuUtilization.cs
@@ -2,8 +2,6 @@
// 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 System.Runtime.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.GetEUid.cs b/src/Common/src/Interop/Unix/System.Native/Interop.GetEUid.cs
index 8b525fa32..c964dec14 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.GetEUid.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.GetEUid.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.GetPid.cs b/src/Common/src/Interop/Unix/System.Native/Interop.GetPid.cs
index 02d259db7..b93f08953 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.GetPid.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.GetPid.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.GetPwUid.cs b/src/Common/src/Interop/Unix/System.Native/Interop.GetPwUid.cs
index 28b2309d0..5a9a23f87 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.GetPwUid.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.GetPwUid.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.GetRandomBytes.cs b/src/Common/src/Interop/Unix/System.Native/Interop.GetRandomBytes.cs
index 858506935..fcc9b8db9 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.GetRandomBytes.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.GetRandomBytes.cs
@@ -2,8 +2,6 @@
// 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.Diagnostics;
using System.Runtime.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.GetSockOpt.cs b/src/Common/src/Interop/Unix/System.Native/Interop.GetSockOpt.cs
index d0a724209..4fd722fd2 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.GetSockOpt.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.GetSockOpt.cs
@@ -15,5 +15,8 @@ internal static partial class Interop
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetSockOpt")]
internal static extern unsafe Error GetSockOpt(IntPtr socket, SocketOptionLevel optionLevel, SocketOptionName optionName, byte* optionValue, int* optionLen);
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetRawSockOpt")]
+ internal static extern unsafe Error GetRawSockOpt(SafeHandle socket, int optionLevel, int optionName, byte* optionValue, int* optionLen);
}
}
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.LockFileRegion.cs b/src/Common/src/Interop/Unix/System.Native/Interop.LockFileRegion.cs
index 1deb9a431..5f92357e8 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.LockFileRegion.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.LockFileRegion.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs b/src/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs
index ee5e42722..e02afa58e 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs
@@ -44,6 +44,7 @@ internal static partial class Interop
bdevfs = 0x62646576,
bfs = 0x1BADFACE,
binfmt_misc = 0x42494E4D,
+ bootfs = 0xA56D3FF9,
btrfs = 0x9123683E,
ceph = 0x00C36400,
cgroupfs = 0x0027E0EB,
@@ -65,6 +66,7 @@ internal static partial class Interop
ext3 = 0xEF53,
ext4 = 0xEF53,
fat = 0x4006,
+ fd = 0xF00D1E,
fhgfs = 0x19830326,
fuse = 0x65735546,
fuseblk = 0x65735546,
@@ -86,6 +88,7 @@ internal static partial class Interop
jffs2 = 0x72B6,
jfs = 0x3153464A,
kafs = 0x6B414653,
+ lofs = 0xEF53, /* loopback filesystem, magic same as ext2 */
logfs = 0xC97E8168,
lustre = 0x0BD00BD0,
minix_old = 0x137F, /* orig. minix */
@@ -227,6 +230,7 @@ internal static partial class Interop
case "bdevfs":
case "befs":
case "bfs":
+ case "bootfs":
case "bpf_fs":
case "btrfs":
case "btrfs_test":
@@ -261,6 +265,7 @@ internal static partial class Interop
case "jffs":
case "jffs2":
case "jfs":
+ case "lofs":
case "logfs":
case "lxfs":
case "minix (30 char.)":
@@ -384,15 +389,18 @@ internal static partial class Interop
case "cgroupfs":
case "cgroup2fs":
case "configfs":
+ case "cpuset":
case "cramfs":
case "cramfs-wend":
case "cryptkeeper":
- case "cpuset":
+ case "ctfs":
case "debugfs":
+ case "dev":
case "devfs":
case "devpts":
case "devtmpfs":
case "encfs":
+ case "fd":
case "fdesc":
case "fuse.gvfsd-fuse":
case "fusectl":
@@ -400,9 +408,11 @@ internal static partial class Interop
case "hugetlbfs":
case "libpam-encfs":
case "ibpam-mount":
+ case "mntfs":
+ case "mqueue":
case "mtpfs":
case "mythtvfs":
- case "mqueue":
+ case "objfs":
case "openprom":
case "openpromfs":
case "pipefs":
@@ -417,6 +427,7 @@ internal static partial class Interop
case "securityfs":
case "selinux":
case "selinuxfs":
+ case "sharefs":
case "sockfs":
case "sysfs":
case "tmpfs":
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.MountPoints.cs b/src/Common/src/Interop/Unix/System.Native/Interop.MountPoints.cs
index 134dcb920..21479bca6 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.MountPoints.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.MountPoints.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Collections.Generic;
using System.Runtime.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.PathConf.cs b/src/Common/src/Interop/Unix/System.Native/Interop.PathConf.cs
index 7213cb026..e2c127772 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.PathConf.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.PathConf.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
internal static partial class Interop
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.Read.cs b/src/Common/src/Interop/Unix/System.Native/Interop.Read.cs
index 233feabdb..e37a2ae6c 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.Read.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.Read.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
-using Microsoft.Win32.SafeHandles;
internal static partial class Interop
{
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.SetSockOpt.cs b/src/Common/src/Interop/Unix/System.Native/Interop.SetSockOpt.cs
index 4c92eccac..e958c9fcc 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.SetSockOpt.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.SetSockOpt.cs
@@ -15,5 +15,8 @@ internal static partial class Interop
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SetSockOpt")]
internal static extern unsafe Error SetSockOpt(IntPtr socket, SocketOptionLevel optionLevel, SocketOptionName optionName, byte* optionValue, int optionLen);
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SetRawSockOpt")]
+ internal static extern unsafe Error SetRawSockOpt(SafeHandle socket, int optionLevel, int optionName, byte* optionValue, int optionLen);
}
}
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs b/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
index d06fbda71..628bd9c70 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
@@ -55,7 +55,7 @@ internal static partial class Interop
}
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FStat", SetLastError = true)]
- internal static extern int FStat(SafeFileHandle fd, out FileStatus output);
+ internal static extern int FStat(SafeHandle fd, out FileStatus output);
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Stat", SetLastError = true)]
internal static extern int Stat(string path, out FileStatus output);
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.SysLog.cs b/src/Common/src/Interop/Unix/System.Native/Interop.SysLog.cs
index 2edde6fbc..8933e9c3d 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.SysLog.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.SysLog.cs
@@ -19,28 +19,6 @@ internal static partial class Interop
LOG_NOTICE = 5, /* normal but significant condition */
LOG_INFO = 6, /* informational */
LOG_DEBUG = 7, /* debug-level messages */
- // Facilities
- LOG_KERN = (0<<3), /* kernel messages */
- LOG_USER = (1<<3), /* random user-level messages */
- LOG_MAIL = (2<<3), /* mail system */
- LOG_DAEMON = (3<<3), /* system daemons */
- LOG_AUTH = (4<<3), /* authorization messages */
- LOG_SYSLOG = (5<<3), /* messages generated internally by syslogd */
- LOG_LPR = (6<<3), /* line printer subsystem */
- LOG_NEWS = (7<<3), /* network news subsystem */
- LOG_UUCP = (8<<3), /* UUCP subsystem */
- LOG_CRON = (9<<3), /* clock daemon */
- LOG_AUTHPRIV = (10<<3), /* authorization messages (private) */
- LOG_FTP = (11<<3), /* ftp daemon */
- // Between FTP and Local is reserved for system use
- LOG_LOCAL0 = (16<<3), /* reserved for local use */
- LOG_LOCAL1 = (17<<3), /* reserved for local use */
- LOG_LOCAL2 = (18<<3), /* reserved for local use */
- LOG_LOCAL3 = (19<<3), /* reserved for local use */
- LOG_LOCAL4 = (20<<3), /* reserved for local use */
- LOG_LOCAL5 = (21<<3), /* reserved for local use */
- LOG_LOCAL6 = (22<<3), /* reserved for local use */
- LOG_LOCAL7 = (23<<3), /* reserved for local use */
}
///
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.Write.cs b/src/Common/src/Interop/Unix/System.Native/Interop.Write.cs
index fb06d463b..9c4e0aa2a 100644
--- a/src/Common/src/Interop/Unix/System.Native/Interop.Write.cs
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.Write.cs
@@ -2,8 +2,8 @@
// 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.InteropServices;
-using Microsoft.Win32.SafeHandles;
internal static partial class Interop
{
@@ -22,6 +22,6 @@ internal static partial class Interop
internal static extern unsafe int Write(SafeHandle fd, byte* buffer, int bufferSize);
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Write", SetLastError = true)]
- internal static extern unsafe int Write(int fd, byte* buffer, int bufferSize);
+ internal static extern unsafe int Write(IntPtr fd, byte* buffer, int bufferSize);
}
}
diff --git a/src/Common/src/Interop/Windows/Advapi32/Interop.CryptImportKey.cs b/src/Common/src/Interop/Windows/Advapi32/Interop.CryptImportKey.cs
index c741fcd8f..d6d1e3b1b 100644
--- a/src/Common/src/Interop/Windows/Advapi32/Interop.CryptImportKey.cs
+++ b/src/Common/src/Interop/Windows/Advapi32/Interop.CryptImportKey.cs
@@ -10,9 +10,9 @@ internal partial class Interop
internal partial class Advapi32
{
[DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
- internal static extern bool CryptImportKey(
+ internal static extern unsafe bool CryptImportKey(
SafeProvHandle hProv,
- byte[] pbData,
+ byte* pbData,
int dwDataLen,
SafeKeyHandle hPubKey,
int dwFlags,
diff --git a/src/Common/src/Interop/Windows/Interop.Libraries.cs b/src/Common/src/Interop/Windows/Interop.Libraries.cs
index 8b4df46ca..1c1b3db6a 100644
--- a/src/Common/src/Interop/Windows/Interop.Libraries.cs
+++ b/src/Common/src/Interop/Windows/Interop.Libraries.cs
@@ -39,5 +39,6 @@ internal static partial class Interop
internal const string CompressionNative = "clrcompression.dll";
internal const string CoreWinRT = "api-ms-win-core-winrt-l1-1-0.dll";
internal const string MsQuic = "msquic.dll";
+ internal const string HostPolicy = "hostpolicy.dll";
}
}
diff --git a/src/Common/src/Interop/Windows/Kernel32/Interop.EnumProcessModules.cs b/src/Common/src/Interop/Windows/Kernel32/Interop.EnumProcessModules.cs
index 15850d212..581188f8f 100644
--- a/src/Common/src/Interop/Windows/Kernel32/Interop.EnumProcessModules.cs
+++ b/src/Common/src/Interop/Windows/Kernel32/Interop.EnumProcessModules.cs
@@ -11,6 +11,6 @@ internal partial class Interop
internal partial class Kernel32
{
[DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "K32EnumProcessModules")]
- internal static extern bool EnumProcessModules(SafeProcessHandle handle, IntPtr modules, int size, ref int needed);
+ internal static extern bool EnumProcessModules(SafeProcessHandle handle, IntPtr[]? modules, int size, out int needed);
}
}
diff --git a/src/Common/src/Interop/Windows/Kernel32/Interop.Globalization.cs b/src/Common/src/Interop/Windows/Kernel32/Interop.Globalization.cs
index 192e555bb..2aa2d6a17 100644
--- a/src/Common/src/Interop/Windows/Kernel32/Interop.Globalization.cs
+++ b/src/Common/src/Interop/Windows/Kernel32/Interop.Globalization.cs
@@ -9,6 +9,15 @@ internal static partial class Interop
{
internal static unsafe partial class Kernel32
{
+ // Under debug mode only, we'll want to check the error codes
+ // of some of the p/invokes we make.
+
+#if DEBUG
+ private const bool SetLastErrorForDebug = true;
+#else
+ private const bool SetLastErrorForDebug = false;
+#endif
+
internal const uint LOCALE_ALLOW_NEUTRAL_NAMES = 0x08000000; // Flag to allow returning neutral names/lcids for name conversion
internal const uint LOCALE_ILANGUAGE = 0x00000001;
internal const uint LOCALE_SUPPLEMENTAL = 0x00000002;
@@ -31,6 +40,10 @@ internal static partial class Interop
internal const uint TIME_NOSECONDS = 0x00000002;
+ internal const int GEOCLASS_NATION = 16;
+ internal const int GEO_ISO2 = 4;
+ internal const int GEOID_NOT_AVAILABLE = -1;
+
internal const string LOCALE_NAME_USER_DEFAULT = null;
internal const string LOCALE_NAME_SYSTEM_DEFAULT = "!x-sys-default-locale";
@@ -40,7 +53,7 @@ internal static partial class Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern int LocaleNameToLCID(string lpName, uint dwFlags);
- [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int LCMapStringEx(
string? lpLocaleName,
uint dwMapFlags,
@@ -52,7 +65,7 @@ internal static partial class Interop
void* lpReserved,
IntPtr sortHandle);
- [DllImport("kernel32.dll", EntryPoint = "FindNLSStringEx")]
+ [DllImport("kernel32.dll", EntryPoint = "FindNLSStringEx", SetLastError = SetLastErrorForDebug)]
internal static extern int FindNLSStringEx(
char* lpLocaleName,
uint dwFindNLSStringFlags,
@@ -85,14 +98,14 @@ internal static partial class Interop
int cchCount2,
bool bIgnoreCase);
- [DllImport("kernel32.dll", EntryPoint = "FindStringOrdinal")]
+ [DllImport("kernel32.dll", EntryPoint = "FindStringOrdinal", SetLastError = SetLastErrorForDebug)]
internal static extern int FindStringOrdinal(
uint dwFindStringOrdinalFlags,
char* lpStringSource,
int cchSource,
char* lpStringValue,
int cchValue,
- int bIgnoreCase);
+ BOOL bIgnoreCase);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern bool IsNLSDefinedString(
@@ -124,6 +137,12 @@ internal static partial class Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern int GetCalendarInfoEx(string? lpLocaleName, uint Calendar, IntPtr lpReserved, uint CalType, IntPtr lpCalData, int cchData, IntPtr lpValue);
+ [DllImport("kernel32.dll")]
+ internal static extern int GetUserGeoID(int geoClass);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
+ internal static extern int GetGeoInfo(int location, int geoType, char* lpGeoData, int cchData, int LangId);
+
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern bool EnumCalendarInfoExEx(EnumCalendarInfoProcExEx pCalInfoEnumProcExEx, string lpLocaleName, uint Calendar, string? lpReserved, uint CalType, void* lParam);
diff --git a/src/Common/src/Interop/Windows/Kernel32/Interop.IsWow64Process_SafeProcessHandle.cs b/src/Common/src/Interop/Windows/Kernel32/Interop.IsWow64Process_SafeProcessHandle.cs
index e5a32facf..dc8626a0a 100644
--- a/src/Common/src/Interop/Windows/Kernel32/Interop.IsWow64Process_SafeProcessHandle.cs
+++ b/src/Common/src/Interop/Windows/Kernel32/Interop.IsWow64Process_SafeProcessHandle.cs
@@ -10,6 +10,6 @@ internal partial class Interop
internal partial class Kernel32
{
[DllImport(Libraries.Kernel32, SetLastError = true)]
- internal static extern bool IsWow64Process(SafeProcessHandle hProcess, ref bool Wow64Process);
+ internal static extern bool IsWow64Process(SafeProcessHandle hProcess, out bool Wow64Process);
}
}
diff --git a/src/Common/src/Interop/Windows/Normaliz/Interop.Normalization.cs b/src/Common/src/Interop/Windows/Normaliz/Interop.Normalization.cs
index 3e954483d..362b7ffb4 100644
--- a/src/Common/src/Interop/Windows/Normaliz/Interop.Normalization.cs
+++ b/src/Common/src/Interop/Windows/Normaliz/Interop.Normalization.cs
@@ -3,21 +3,21 @@
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
+using System.Text;
internal static partial class Interop
{
internal static partial class Normaliz
{
[DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- internal static extern bool IsNormalizedString(int normForm, string source, int length);
+ internal static extern unsafe BOOL IsNormalizedString(NormalizationForm normForm, char* source, int length);
[DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- internal static extern int NormalizeString(
- int normForm,
- string source,
+ internal static extern unsafe int NormalizeString(
+ NormalizationForm normForm,
+ char* source,
int sourceLength,
- [System.Runtime.InteropServices.OutAttribute]
- char[]? destination,
+ char* destination,
int destinationLength);
}
}
diff --git a/src/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs b/src/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs
index 2d51c44f5..413efdfbf 100644
--- a/src/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs
+++ b/src/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs
@@ -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.
@@ -7,15 +7,16 @@ internal static partial class Interop
internal class StatusOptions
{
// Error codes from ntstatus.h
- internal const uint STATUS_SUCCESS = 0x00000000;
- internal const uint STATUS_SOME_NOT_MAPPED = 0x00000107;
- internal const uint STATUS_NO_MORE_FILES = 0x80000006;
- internal const uint STATUS_INVALID_PARAMETER = 0xC000000D;
- internal const uint STATUS_NO_MEMORY = 0xC0000017;
- internal const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034;
- internal const uint STATUS_NONE_MAPPED = 0xC0000073;
+ internal const uint STATUS_SUCCESS = 0x00000000;
+ internal const uint STATUS_SOME_NOT_MAPPED = 0x00000107;
+ internal const uint STATUS_NO_MORE_FILES = 0x80000006;
+ internal const uint STATUS_INVALID_PARAMETER = 0xC000000D;
+ internal const uint STATUS_FILE_NOT_FOUND = 0xC000000F;
+ internal const uint STATUS_NO_MEMORY = 0xC0000017;
+ internal const uint STATUS_ACCESS_DENIED = 0xC0000022;
+ internal const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034;
+ internal const uint STATUS_ACCOUNT_RESTRICTION = 0xC000006E;
+ internal const uint STATUS_NONE_MAPPED = 0xC0000073;
internal const uint STATUS_INSUFFICIENT_RESOURCES = 0xC000009A;
- internal const uint STATUS_ACCESS_DENIED = 0xC0000022;
- internal const uint STATUS_ACCOUNT_RESTRICTION = 0xc000006e;
}
}
diff --git a/src/Common/src/Interop/Windows/NtDll/Interop.RtlGetVersion.cs b/src/Common/src/Interop/Windows/NtDll/Interop.RtlGetVersion.cs
index 17f052150..d01a65913 100644
--- a/src/Common/src/Interop/Windows/NtDll/Interop.RtlGetVersion.cs
+++ b/src/Common/src/Interop/Windows/NtDll/Interop.RtlGetVersion.cs
@@ -18,19 +18,15 @@ internal partial class Interop
return RtlGetVersion(ref osvi);
}
- internal static unsafe string RtlGetVersion()
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal unsafe struct RTL_OSVERSIONINFOEX
{
- const string Version = "Microsoft Windows";
- if (RtlGetVersionEx(out RTL_OSVERSIONINFOEX osvi) == 0)
- {
- return osvi.szCSDVersion[0] != '\0' ?
- string.Format("{0} {1}.{2}.{3} {4}", Version, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber, new string(&(osvi.szCSDVersion[0]))) :
- string.Format("{0} {1}.{2}.{3}", Version, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
- }
- else
- {
- return Version;
- }
+ internal uint dwOSVersionInfoSize;
+ internal uint dwMajorVersion;
+ internal uint dwMinorVersion;
+ internal uint dwBuildNumber;
+ internal uint dwPlatformId;
+ internal fixed char szCSDVersion[128];
}
}
}
diff --git a/src/Common/src/System/Diagnostics/NetFrameworkUtils.cs b/src/Common/src/System/Diagnostics/NetFrameworkUtils.cs
index 59d028c5a..de9f756c1 100644
--- a/src/Common/src/System/Diagnostics/NetFrameworkUtils.cs
+++ b/src/Common/src/System/Diagnostics/NetFrameworkUtils.cs
@@ -220,7 +220,7 @@ namespace System.Diagnostics
StringBuilder installBuilder = new StringBuilder();
installBuilder.Append(installRoot);
if (!installRoot.EndsWith("\\", StringComparison.Ordinal))
- installBuilder.Append("\\");
+ installBuilder.Append('\\');
installBuilder.Append(version);
dllDir = installBuilder.ToString();
}
diff --git a/src/Common/src/System/Diagnostics/TraceListenerHelpers.cs b/src/Common/src/System/Diagnostics/TraceListenerHelpers.cs
index 6e5bbfa07..4ffd68059 100644
--- a/src/Common/src/System/Diagnostics/TraceListenerHelpers.cs
+++ b/src/Common/src/System/Diagnostics/TraceListenerHelpers.cs
@@ -2,11 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#nullable enable
namespace System.Diagnostics
{
internal static partial class TraceListenerHelpers
{
- private static volatile string s_processName;
+ private static volatile string? s_processName;
internal static int GetThreadId()
{
diff --git a/src/Common/src/System/IO/ChunkedMemoryStream.cs b/src/Common/src/System/IO/ChunkedMemoryStream.cs
index a7295c4c2..ceb003f86 100644
--- a/src/Common/src/System/IO/ChunkedMemoryStream.cs
+++ b/src/Common/src/System/IO/ChunkedMemoryStream.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#nullable enable
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
@@ -11,8 +12,8 @@ namespace System.IO
/// Provides an in-memory stream composed of non-contiguous chunks.
internal sealed class ChunkedMemoryStream : Stream
{
- private MemoryChunk _headChunk;
- private MemoryChunk _currentChunk;
+ private MemoryChunk? _headChunk;
+ private MemoryChunk? _currentChunk;
private const int InitialChunkDefaultSize = 1024;
private const int MaxChunkSize = 1024 * InitialChunkDefaultSize;
@@ -24,7 +25,7 @@ namespace System.IO
{
byte[] result = new byte[_totalLength];
int offset = 0;
- for (MemoryChunk chunk = _headChunk; chunk != null; chunk = chunk._next)
+ for (MemoryChunk? chunk = _headChunk; chunk != null; chunk = chunk._next)
{
Debug.Assert(chunk._next == null || chunk._freeOffset == chunk._buffer.Length);
Buffer.BlockCopy(chunk._buffer, 0, result, offset, chunk._freeOffset);
@@ -109,7 +110,7 @@ namespace System.IO
{
internal readonly byte[] _buffer;
internal int _freeOffset;
- internal MemoryChunk _next;
+ internal MemoryChunk? _next;
internal MemoryChunk(int bufferSize) { _buffer = new byte[bufferSize]; }
}
diff --git a/src/Common/src/System/IO/DelegatingStream.cs b/src/Common/src/System/IO/DelegatingStream.cs
index 889bbb213..cbb3075a1 100644
--- a/src/Common/src/System/IO/DelegatingStream.cs
+++ b/src/Common/src/System/IO/DelegatingStream.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#nullable enable
using System.Diagnostics;
using System.IO;
using System.Threading;
@@ -113,7 +114,7 @@ namespace System.Net.Http
return _innerStream.ReadAsync(buffer, cancellationToken);
}
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return _innerStream.BeginRead(buffer, offset, count, callback, state);
}
@@ -167,7 +168,7 @@ namespace System.Net.Http
return _innerStream.WriteAsync(buffer, cancellationToken);
}
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return _innerStream.BeginWrite(buffer, offset, count, callback, state);
}
diff --git a/src/Common/src/System/IO/FileSystem.Attributes.Windows.cs b/src/Common/src/System/IO/FileSystem.Attributes.Windows.cs
index 977293e3f..274f049f5 100644
--- a/src/Common/src/System/IO/FileSystem.Attributes.Windows.cs
+++ b/src/Common/src/System/IO/FileSystem.Attributes.Windows.cs
@@ -81,7 +81,7 @@ namespace System.IO
)
{
// Assert so we can track down other cases (if any) to add to our test suite
- Debug.Assert(errorCode == Interop.Errors.ERROR_ACCESS_DENIED || errorCode == Interop.Errors.ERROR_SHARING_VIOLATION,
+ Debug.Assert(errorCode == Interop.Errors.ERROR_ACCESS_DENIED || errorCode == Interop.Errors.ERROR_SHARING_VIOLATION || errorCode == Interop.Errors.ERROR_SEM_TIMEOUT,
$"Unexpected error code getting attributes {errorCode} from path {path}");
// Files that are marked for deletion will not let you GetFileAttributes,
diff --git a/src/Common/src/System/IO/ReadOnlyMemoryStream.cs b/src/Common/src/System/IO/ReadOnlyMemoryStream.cs
index 5ec499dc6..6c826601a 100644
--- a/src/Common/src/System/IO/ReadOnlyMemoryStream.cs
+++ b/src/Common/src/System/IO/ReadOnlyMemoryStream.cs
@@ -105,7 +105,7 @@ namespace System.IO
new ValueTask(Task.FromCanceled(cancellationToken)) :
new ValueTask(Read(buffer.Span));
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) =>
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) =>
TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state);
public override int EndRead(IAsyncResult asyncResult) =>
diff --git a/src/Common/src/System/IO/RowConfigReader.cs b/src/Common/src/System/IO/RowConfigReader.cs
index fcd9d10db..b59fe765b 100644
--- a/src/Common/src/System/IO/RowConfigReader.cs
+++ b/src/Common/src/System/IO/RowConfigReader.cs
@@ -2,7 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#nullable enable
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace System.IO
@@ -47,8 +49,7 @@ namespace System.IO
///
public string GetNextValue(string key)
{
- string value;
- if (!TryGetNextValue(key, out value))
+ if (!TryGetNextValue(key, out string? value))
{
throw new InvalidOperationException("Couldn't get next value with key " + key);
}
@@ -62,7 +63,7 @@ namespace System.IO
/// Tries to get the next occurrence of the given key from the current position of the reader.
/// If successful, returns true and stores the result in 'value'. Otherwise, returns false.
///
- public bool TryGetNextValue(string key, out string value)
+ public bool TryGetNextValue(string key, [NotNullWhen(true)] out string? value)
{
Debug.Assert(_buffer != null);
if (_currentIndex >= _buffer.Length)
diff --git a/src/Common/src/System/Runtime/ExceptionServices/ExceptionStackTrace.cs b/src/Common/src/System/Runtime/ExceptionServices/ExceptionStackTrace.cs
index c8b633ca7..57647f3ab 100644
--- a/src/Common/src/System/Runtime/ExceptionServices/ExceptionStackTrace.cs
+++ b/src/Common/src/System/Runtime/ExceptionServices/ExceptionStackTrace.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#nullable enable
using System.Diagnostics;
using System.Reflection;
@@ -16,12 +17,12 @@ namespace System.Runtime.ExceptionServices
Debug.Assert(exception != null, "Expected non-null Exception");
const string ExceptionRemoteStackTraceStringName = "_remoteStackTraceString";
- FieldInfo fi = typeof(Exception).GetField(ExceptionRemoteStackTraceStringName, BindingFlags.NonPublic | BindingFlags.Instance);
+ FieldInfo? fi = typeof(Exception).GetField(ExceptionRemoteStackTraceStringName, BindingFlags.NonPublic | BindingFlags.Instance);
if (fi != null)
{
string text =
- (string)fi.GetValue(exception) +
+ (string?)fi.GetValue(exception) +
Environment.StackTrace + Environment.NewLine +
"--- End of stack trace from AddCurrentStack ---" + Environment.NewLine;
fi.SetValue(exception, text);
diff --git a/src/Common/src/System/StrongToWeakReference.cs b/src/Common/src/System/StrongToWeakReference.cs
index f49a9f13d..83dfd05dc 100644
--- a/src/Common/src/System/StrongToWeakReference.cs
+++ b/src/Common/src/System/StrongToWeakReference.cs
@@ -9,7 +9,7 @@ namespace System
/// Provides an object wrapper that can transition between strong and weak references to the object.
internal sealed class StrongToWeakReference : WeakReference where T : class
{
- private T _strongRef;
+ private T? _strongRef;
/// Initializes the instance with a strong reference to the specified object.
/// The object to wrap.
@@ -30,9 +30,9 @@ namespace System
}
/// Gets the wrapped object.
- public new T Target => _strongRef ?? WeakTarget;
+ public new T? Target => _strongRef ?? WeakTarget;
/// Gets the wrapped object via its weak reference.
- private T WeakTarget => base.Target as T;
+ private T? WeakTarget => base.Target as T;
}
}
diff --git a/src/Common/src/System/Text/StringBuilderCache.cs b/src/Common/src/System/Text/StringBuilderCache.cs
index aac2c2e8a..4d96cb301 100644
--- a/src/Common/src/System/Text/StringBuilderCache.cs
+++ b/src/Common/src/System/Text/StringBuilderCache.cs
@@ -11,7 +11,7 @@ namespace System.Text
// The value 360 was chosen in discussion with performance experts as a compromise between using
// as litle memory per thread as possible and still covering a large part of short-lived
// StringBuilder creations on the startup path of VS designers.
- private const int MaxBuilderSize = 360;
+ internal const int MaxBuilderSize = 360;
private const int DefaultCapacity = 16; // == StringBuilder.DefaultCapacity
// WARNING: We allow diagnostic tools to directly inspect this member (t_cachedInstance).
diff --git a/src/Common/src/System/Threading/Tasks/RendezvousAwaitable.cs b/src/Common/src/System/Threading/Tasks/RendezvousAwaitable.cs
index 84134884f..890c1ab75 100644
--- a/src/Common/src/System/Threading/Tasks/RendezvousAwaitable.cs
+++ b/src/Common/src/System/Threading/Tasks/RendezvousAwaitable.cs
@@ -2,7 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#nullable enable
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
@@ -19,11 +21,11 @@ namespace System.Threading.Tasks
/// The continuation to invoke when the operation completes, or if the operation
/// has completed before OnCompleted is called.
///
- private Action _continuation;
+ private Action? _continuation;
/// The exception representing the failed async operation, if it failed.
- private ExceptionDispatchInfo _error;
+ private ExceptionDispatchInfo? _error;
/// The result of the async operation, if it succeeded.
- private TResult _result;
+ [AllowNull] private TResult _result = default;
#if DEBUG
private bool _resultSet;
#endif
@@ -39,7 +41,7 @@ namespace System.Threading.Tasks
{
get
{
- Action c = Volatile.Read(ref _continuation);
+ Action? c = Volatile.Read(ref _continuation);
Debug.Assert(c == null || c == s_completionSentinel);
return c != null;
}
@@ -55,7 +57,7 @@ namespace System.Threading.Tasks
// Propagate any error if there is one, clearing it out first to prepare for reuse.
// We don't need to clear a result, as result and error are mutually exclusive.
- ExceptionDispatchInfo error = _error;
+ ExceptionDispatchInfo? error = _error;
if (error != null)
{
_error = null;
@@ -101,7 +103,7 @@ namespace System.Threading.Tasks
/// Alerts any awaiter that the operation has completed.
private void NotifyAwaiter()
{
- Action c = _continuation ?? Interlocked.CompareExchange(ref _continuation, s_completionSentinel, null);
+ Action? c = _continuation ?? Interlocked.CompareExchange(ref _continuation, s_completionSentinel, null);
if (c != null)
{
Debug.Assert(c != s_completionSentinel);
@@ -122,7 +124,7 @@ namespace System.Threading.Tasks
{
Debug.Assert(continuation != null);
- Action c = _continuation ?? Interlocked.CompareExchange(ref _continuation, continuation, null);
+ Action? c = _continuation ?? Interlocked.CompareExchange(ref _continuation, continuation, null);
if (c != null)
{
Debug.Assert(c == s_completionSentinel);
diff --git a/src/Framework/Framework.depproj b/src/Framework/Framework.depproj
index efed62681..386946a02 100644
--- a/src/Framework/Framework.depproj
+++ b/src/Framework/Framework.depproj
@@ -71,6 +71,8 @@
<_filteredNuGetDeployByFileName Include="@(_nuGetDeployByFileName)" Condition="'%(Identity)' == 'netstandard'" />
<_filteredNuGetDeployByFileName Include="@(_nuGetDeployByFileName)" Condition="'%(Identity)' == 'mscorlib'" />
+ <_filteredNuGetDeployByFileName Include="@(_nuGetDeployByFileName)" Condition="$([System.String]::new('%(Identity)').StartsWith('libSystem'))" />
+
<_filteredNuGetDeployByFileName Include="@(_nuGetDeployByFileName)" Condition="'%(Identity)' == 'clrcompression'" />
diff --git a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
index d3430c6cd..daab1bca5 100644
--- a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
+++ b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
@@ -589,6 +589,16 @@ namespace ILCompiler.CppCodeGen
sb.AppendLine();
AppendCppMethodDeclaration(sb, method, true);
sb.AppendLine();
+
+ // TODO: workaround unreachable globalization methods
+ string moduleName = method.GetPInvokeMethodMetadata().Module;
+ if (moduleName == (_compilation.TypeSystemContext.Target.IsWindows ? "libSystem.Globalization.Native" : "kernel32.dll"))
+ {
+ sb.Append("{ throw 0; }");
+ methodCodeNodeNeedingCode.SetCode(sb.ToString(), Array.Empty
@@ -1165,9 +1245,6 @@
Common\Interop\Windows\Crypt32\Interop.CryptProtectMemory.cs
-
- Common\Interop\Windows\Interop.BOOL.cs
-
Common\Interop\Windows\Interop.BOOLEAN.cs
@@ -1282,12 +1359,6 @@
Common\Interop\Windows\Kernel32\Interop.GetTempPathW.cs
-
- Common\Interop\Windows\Kernel32\Interop.GetVersionExW.cs
-
-
- Common\Interop\Windows\Kernel32\Interop.Globalization.cs
-
Common\Interop\Windows\Kernel32\Interop.GlobalMemoryStatusEx.cs
@@ -1336,9 +1407,6 @@
Common\Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_NativeOverlapped.cs
-
- Common\Interop\Windows\Kernel32\Interop.ResolveLocaleName.cs
-
Common\Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs
@@ -1399,18 +1467,15 @@
Common\Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_NativeOverlapped.cs
-
- Common\Interop\Windows\Normaliz\Interop.Idna.cs
-
-
- Common\Interop\Windows\Normaliz\Interop.Normalization.cs
-
Common\Interop\Windows\NtDll\Interop.NtQueryInformationFile.cs
Common\Interop\Windows\NtDll\Interop.NtQuerySystemInformation.cs
+
+ Common\Interop\Windows\NtDll\Interop.RtlGetVersion.cs
+
Common\Interop\Windows\NtDll\Interop.SYSTEM_LEAP_SECOND_INFORMATION.cs
@@ -1455,15 +1520,8 @@
-
-
-
-
+
-
-
-
-
@@ -1486,6 +1544,9 @@
+
+
+
@@ -1500,7 +1561,6 @@
-
Common\Interop\Windows\OleAut32\Interop.SysAllocStringLen.cs
@@ -1545,7 +1605,7 @@
Common\System\IO\Win32Marshal.cs
-
+
@@ -1566,36 +1626,6 @@
Common\Interop\Unix\Interop.Libraries.cs
-
- Common\Interop\Unix\System.Globalization.Native\Interop.Calendar.cs
-
-
- Common\Interop\Unix\System.Globalization.Native\Interop.Casing.cs
-
-
- Common\Interop\Unix\System.Globalization.Native\Interop.Collation.cs
-
-
- Common\Interop\Unix\System.Globalization.Native\Interop.ICU.cs
-
-
- Common\Interop\Unix\System.Globalization.Native\Interop.Idna.cs
-
-
- Common\Interop\Unix\System.Globalization.Native\Interop.Locale.cs
-
-
- Common\Interop\Unix\System.Globalization.Native\Interop.Normalization.cs
-
-
- Common\Interop\Unix\System.Globalization.Native\Interop.ResultCode.cs
-
-
- Common\Interop\Unix\System.Globalization.Native\Interop.TimeZoneInfo.cs
-
-
- Common\Interop\Unix\System.Globalization.Native\Interop.Utils.cs
-
Common\Interop\Unix\System.Native\Interop.Access.cs
@@ -1705,22 +1735,15 @@
+
-
-
-
-
+
-
-
-
-
-
-
-
+
+
diff --git a/src/System.Private.CoreLib/shared/System/AppContext.cs b/src/System.Private.CoreLib/shared/System/AppContext.cs
index c79e8ee9f..907356696 100644
--- a/src/System.Private.CoreLib/shared/System/AppContext.cs
+++ b/src/System.Private.CoreLib/shared/System/AppContext.cs
@@ -22,7 +22,7 @@ namespace System
public static string BaseDirectory =>
// The value of APP_CONTEXT_BASE_DIRECTORY key has to be a string and it is not allowed to be any other type.
// Otherwise the caller will get invalid cast exception
- (string?)GetData("APP_CONTEXT_BASE_DIRECTORY") ??
+ GetData("APP_CONTEXT_BASE_DIRECTORY") as string ??
(s_defaultBaseDirectory ??= GetBaseDirectoryCore());
public static string? TargetFrameworkName =>
diff --git a/src/System.Private.CoreLib/shared/System/AppContextConfigHelper.cs b/src/System.Private.CoreLib/shared/System/AppContextConfigHelper.cs
index a55076348..4bb3e180d 100644
--- a/src/System.Private.CoreLib/shared/System/AppContextConfigHelper.cs
+++ b/src/System.Private.CoreLib/shared/System/AppContextConfigHelper.cs
@@ -12,7 +12,7 @@ namespace System
{
try
{
- object config = AppContext.GetData(configName);
+ object? config = AppContext.GetData(configName);
int result = defaultValue;
switch (config)
{
@@ -54,7 +54,7 @@ namespace System
{
try
{
- object config = AppContext.GetData(configName);
+ object? config = AppContext.GetData(configName);
short result = defaultValue;
switch (config)
{
diff --git a/src/System.Private.CoreLib/shared/System/AppDomain.cs b/src/System.Private.CoreLib/shared/System/AppDomain.cs
index 68379b235..7cd1e8e97 100644
--- a/src/System.Private.CoreLib/shared/System/AppDomain.cs
+++ b/src/System.Private.CoreLib/shared/System/AppDomain.cs
@@ -149,8 +149,7 @@ namespace System
public bool? IsCompatibilitySwitchSet(string value)
{
- bool result;
- return AppContext.TryGetSwitch(value, out result) ? result : default(bool?);
+ return AppContext.TryGetSwitch(value, out bool result) ? result : default(bool?);
}
public bool IsDefaultAppDomain() => true;
diff --git a/src/System.Private.CoreLib/shared/System/Array.cs b/src/System.Private.CoreLib/shared/System/Array.cs
index 5e22383eb..fa80d26e4 100644
--- a/src/System.Private.CoreLib/shared/System/Array.cs
+++ b/src/System.Private.CoreLib/shared/System/Array.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -26,6 +27,11 @@ namespace System
internal const int MaxArrayLength = 0X7FEFFFFF;
internal const int MaxByteArrayLength = 0x7FFFFFC7;
+ // This is the threshold where Introspective sort switches to Insertion sort.
+ // Empirically, 16 seems to speed up most cases without slowing down others, at least for integers.
+ // Large value types may benefit from a smaller number.
+ internal const int IntrosortSizeThreshold = 16;
+
// This ctor exists solely to prevent C# from generating a protected .ctor that violates the surface area.
private protected Array() { }
@@ -510,9 +516,7 @@ namespace System
if (comparer == Comparer.Default)
{
CorElementType et = array.GetCorElementTypeOfElementType();
- if (et.IsPrimitiveType()
- // IntPtr/UIntPtr does not implement IComparable
- && (et != CorElementType.ELEMENT_TYPE_I) && (et != CorElementType.ELEMENT_TYPE_U))
+ if (et.IsPrimitiveType())
{
if (value == null)
return ~index;
@@ -538,15 +542,27 @@ namespace System
result = GenericBinarySearch(array, adjustedIndex, length, value);
break;
case CorElementType.ELEMENT_TYPE_I4:
+#if TARGET_32BIT
+ case CorElementType.ELEMENT_TYPE_I:
+#endif
result = GenericBinarySearch(array, adjustedIndex, length, value);
break;
case CorElementType.ELEMENT_TYPE_U4:
+#if TARGET_32BIT
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
result = GenericBinarySearch(array, adjustedIndex, length, value);
break;
case CorElementType.ELEMENT_TYPE_I8:
+#if TARGET_64BIT
+ case CorElementType.ELEMENT_TYPE_I:
+#endif
result = GenericBinarySearch(array, adjustedIndex, length, value);
break;
case CorElementType.ELEMENT_TYPE_U8:
+#if TARGET_64BIT
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
result = GenericBinarySearch(array, adjustedIndex, length, value);
break;
case CorElementType.ELEMENT_TYPE_R4:
@@ -1674,15 +1690,27 @@ namespace System
GenericSort(keys, items, adjustedIndex, length);
return;
case CorElementType.ELEMENT_TYPE_I4:
+#if TARGET_32BIT
+ case CorElementType.ELEMENT_TYPE_I:
+#endif
GenericSort(keys, items, adjustedIndex, length);
return;
case CorElementType.ELEMENT_TYPE_U4:
+#if TARGET_32BIT
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
GenericSort(keys, items, adjustedIndex, length);
return;
case CorElementType.ELEMENT_TYPE_I8:
+#if TARGET_64BIT
+ case CorElementType.ELEMENT_TYPE_I:
+#endif
GenericSort(keys, items, adjustedIndex, length);
return;
case CorElementType.ELEMENT_TYPE_U8:
+#if TARGET_64BIT
+ case CorElementType.ELEMENT_TYPE_U:
+#endif
GenericSort(keys, items, adjustedIndex, length);
return;
case CorElementType.ELEMENT_TYPE_R4:
@@ -1691,10 +1719,6 @@ namespace System
case CorElementType.ELEMENT_TYPE_R8:
GenericSort(keys, items, adjustedIndex, length);
return;
- case CorElementType.ELEMENT_TYPE_I:
- case CorElementType.ELEMENT_TYPE_U:
- // IntPtr/UIntPtr does not implement IComparable
- break;
}
static void GenericSort(Array keys, Array? items, int adjustedIndex, int length) where T: struct
@@ -1898,11 +1922,11 @@ namespace System
try
{
- IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length));
+ IntroSort(left, length + left - 1, 2 * (BitOperations.Log2((uint)length) + 1));
}
catch (IndexOutOfRangeException)
{
- IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ ThrowHelper.ThrowArgumentException_BadComparer(comparer);
}
catch (Exception e)
{
@@ -1912,20 +1936,22 @@ namespace System
private void IntroSort(int lo, int hi, int depthLimit)
{
+ Debug.Assert(hi >= lo);
+ Debug.Assert(depthLimit >= 0);
+
while (hi > lo)
{
int partitionSize = hi - lo + 1;
- if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ if (partitionSize <= IntrosortSizeThreshold)
{
- if (partitionSize == 1)
- {
- return;
- }
+ Debug.Assert(partitionSize >= 2);
+
if (partitionSize == 2)
{
SwapIfGreater(lo, hi);
return;
}
+
if (partitionSize == 3)
{
SwapIfGreater(lo, hi - 1);
@@ -1953,8 +1979,11 @@ namespace System
private int PickPivotAndPartition(int lo, int hi)
{
+ Debug.Assert(hi - lo >= IntrosortSizeThreshold);
+
// Compute median-of-three. But also partition them, since we've done the comparison.
int mid = lo + (hi - lo) / 2;
+
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
SwapIfGreater(lo, mid);
SwapIfGreater(lo, hi);
@@ -1976,7 +2005,10 @@ namespace System
}
// Put pivot in the right location.
- Swap(left, hi - 1);
+ if (left != hi - 1)
+ {
+ Swap(left, hi - 1);
+ }
return left;
}
@@ -2104,11 +2136,11 @@ namespace System
try
{
- IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length));
+ IntroSort(left, length + left - 1, 2 * (BitOperations.Log2((uint)length) + 1));
}
catch (IndexOutOfRangeException)
{
- IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ ThrowHelper.ThrowArgumentException_BadComparer(comparer);
}
catch (Exception e)
{
@@ -2118,20 +2150,22 @@ namespace System
private void IntroSort(int lo, int hi, int depthLimit)
{
+ Debug.Assert(hi >= lo);
+ Debug.Assert(depthLimit >= 0);
+
while (hi > lo)
{
int partitionSize = hi - lo + 1;
- if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ if (partitionSize <= IntrosortSizeThreshold)
{
- if (partitionSize == 1)
- {
- return;
- }
+ Debug.Assert(partitionSize >= 2);
+
if (partitionSize == 2)
{
SwapIfGreater(lo, hi);
return;
}
+
if (partitionSize == 3)
{
SwapIfGreater(lo, hi - 1);
@@ -2159,6 +2193,8 @@ namespace System
private int PickPivotAndPartition(int lo, int hi)
{
+ Debug.Assert(hi - lo >= IntrosortSizeThreshold);
+
// Compute median-of-three. But also partition them, since we've done the comparison.
int mid = lo + (hi - lo) / 2;
@@ -2182,7 +2218,10 @@ namespace System
}
// Put pivot in the right location.
- Swap(left, hi - 1);
+ if (left != hi - 1)
+ {
+ Swap(left, hi - 1);
+ }
return left;
}
diff --git a/src/System.Private.CoreLib/shared/System/BitConverter.cs b/src/System.Private.CoreLib/shared/System/BitConverter.cs
index 25719466d..bf604f41a 100644
--- a/src/System.Private.CoreLib/shared/System/BitConverter.cs
+++ b/src/System.Private.CoreLib/shared/System/BitConverter.cs
@@ -4,6 +4,8 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
using Internal.Runtime.CompilerServices;
@@ -449,24 +451,52 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe long DoubleToInt64Bits(double value)
{
+ // Workaround for https://github.com/dotnet/runtime/issues/11413
+ if (Sse2.X64.IsSupported)
+ {
+ Vector128 vec = Vector128.CreateScalarUnsafe(value).AsInt64();
+ return Sse2.X64.ConvertToInt64(vec);
+ }
+
return *((long*)&value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe double Int64BitsToDouble(long value)
{
+ // Workaround for https://github.com/dotnet/runtime/issues/11413
+ if (Sse2.X64.IsSupported)
+ {
+ Vector128 vec = Vector128.CreateScalarUnsafe(value).AsDouble();
+ return vec.ToScalar();
+ }
+
return *((double*)&value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe int SingleToInt32Bits(float value)
{
+ // Workaround for https://github.com/dotnet/runtime/issues/11413
+ if (Sse2.IsSupported)
+ {
+ Vector128 vec = Vector128.CreateScalarUnsafe(value).AsInt32();
+ return Sse2.ConvertToInt32(vec);
+ }
+
return *((int*)&value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe float Int32BitsToSingle(int value)
{
+ // Workaround for https://github.com/dotnet/runtime/issues/11413
+ if (Sse2.IsSupported)
+ {
+ Vector128 vec = Vector128.CreateScalarUnsafe(value).AsSingle();
+ return vec.ToScalar();
+ }
+
return *((float*)&value);
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Boolean.cs b/src/System.Private.CoreLib/shared/System/Boolean.cs
index aad5656aa..71a498d24 100644
--- a/src/System.Private.CoreLib/shared/System/Boolean.cs
+++ b/src/System.Private.CoreLib/shared/System/Boolean.cs
@@ -12,6 +12,7 @@
**
===========================================================*/
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
@@ -223,7 +224,7 @@ namespace System
// Determines whether a String represents true or false.
//
- public static bool TryParse(string? value, out bool result)
+ public static bool TryParse([NotNullWhen(true)] string? value, out bool result)
{
if (value == null)
{
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/ParserHelpers.cs b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/ParserHelpers.cs
index d5f50473f..5d0562e9d 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/ParserHelpers.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/ParserHelpers.cs
@@ -2,7 +2,10 @@
// 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.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
+using Internal.Runtime.CompilerServices;
namespace System.Buffers.Text
{
@@ -70,5 +73,23 @@ namespace System.Buffers.Text
value = default;
return TryParseThrowFormatException(out bytesConsumed);
}
+
+ //
+ // Enable use of ThrowHelper from TryParse() routines without introducing dozens of non-code-coveraged "value= default; bytesConsumed = 0; return false" boilerplate.
+ //
+ [DoesNotReturn]
+ [StackTraceHidden]
+ public static bool TryParseThrowFormatException(ReadOnlySpan source, out T value, out int bytesConsumed) where T : struct
+ {
+ // The parameters to this method are ordered the same as our callers' parameters
+ // allowing the JIT to avoid unnecessary register swapping or spilling.
+
+ Unsafe.SkipInit(out value); // bypass language initialization rules since we're about to throw
+ Unsafe.SkipInit(out bytesConsumed);
+ ThrowHelper.ThrowFormatException_BadFormatSpecifier();
+
+ Debug.Fail("Control should never reach this point.");
+ return false;
+ }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Boolean.cs b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Boolean.cs
index 1c5e4f243..ae771e9b7 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Boolean.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Boolean.cs
@@ -30,7 +30,7 @@ namespace System.Buffers.Text
public static bool TryParse(ReadOnlySpan source, out bool value, out int bytesConsumed, char standardFormat = default)
{
if (!(standardFormat == default(char) || standardFormat == 'G' || standardFormat == 'l'))
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ ThrowHelper.ThrowFormatException_BadFormatSpecifier();
if (source.Length >= 4)
{
@@ -42,7 +42,7 @@ namespace System.Buffers.Text
return true;
}
- if (source.Length >= 5)
+ if (4 < (uint)source.Length)
{
if (dw == 0x534c4146 /* 'SLAF' */ && (source[4] & ~0x20) == 'E')
{
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Guid.cs b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Guid.cs
index cdc2f5e5c..e5b48387d 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Guid.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Guid.cs
@@ -29,19 +29,25 @@ namespace System.Buffers.Text
///
public static bool TryParse(ReadOnlySpan source, out Guid value, out int bytesConsumed, char standardFormat = default)
{
+ FastPath:
+ if (standardFormat == default)
+ {
+ return TryParseGuidCore(source, out value, out bytesConsumed, ends: 0);
+ }
+
switch (standardFormat)
{
- case default(char):
case 'D':
- return TryParseGuidCore(source, false, ' ', ' ', out value, out bytesConsumed);
+ standardFormat = default;
+ goto FastPath;
case 'B':
- return TryParseGuidCore(source, true, '{', '}', out value, out bytesConsumed);
+ return TryParseGuidCore(source, out value, out bytesConsumed, ends: '{' | ('}' << 8));
case 'P':
- return TryParseGuidCore(source, true, '(', ')', out value, out bytesConsumed);
+ return TryParseGuidCore(source, out value, out bytesConsumed, ends: '(' | (')' << 8));
case 'N':
return TryParseGuidN(source, out value, out bytesConsumed);
default:
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(source, out value, out bytesConsumed);
}
}
@@ -97,9 +103,9 @@ namespace System.Buffers.Text
}
// {8-4-4-4-12}, where number is the number of hex digits, and {/} are ends.
- private static bool TryParseGuidCore(ReadOnlySpan source, bool ends, char begin, char end, out Guid value, out int bytesConsumed)
+ private static bool TryParseGuidCore(ReadOnlySpan source, out Guid value, out int bytesConsumed, int ends)
{
- int expectedCodingUnits = 36 + (ends ? 2 : 0); // 32 hex digits + 4 delimiters + 2 optional ends
+ int expectedCodingUnits = 36 + ((ends != 0) ? 2 : 0); // 32 hex digits + 4 delimiters + 2 optional ends
if (source.Length < expectedCodingUnits)
{
@@ -108,9 +114,16 @@ namespace System.Buffers.Text
return false;
}
- if (ends)
+ // The 'ends' parameter is a 16-bit value where the byte denoting the starting
+ // brace is at byte position 0 and the byte denoting the closing brace is at
+ // byte position 1. If no braces are expected, has value 0.
+ // Default: ends = 0
+ // Braces: ends = "}{"
+ // Parens: ends = ")("
+
+ if (ends != 0)
{
- if (source[0] != begin)
+ if (source[0] != (byte)ends)
{
value = default;
bytesConsumed = 0;
@@ -118,6 +131,7 @@ namespace System.Buffers.Text
}
source = source.Slice(1); // skip beginning
+ ends >>= 8; // shift the closing brace to byte position 0
}
if (!TryParseUInt32X(source, out uint i1, out int justConsumed))
@@ -226,7 +240,7 @@ namespace System.Buffers.Text
return false; // 12 digits
}
- if (ends && source[justConsumed] != end)
+ if (ends != 0 && source[justConsumed] != (byte)ends)
{
value = default;
bytesConsumed = 0;
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.cs b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.cs
index d6cc01b8a..10e8bd0a2 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.cs
@@ -35,26 +35,31 @@ namespace System.Buffers.Text
[CLSCompliant(false)]
public static bool TryParse(ReadOnlySpan source, out sbyte value, out int bytesConsumed, char standardFormat = default)
{
- switch (standardFormat)
+ FastPath:
+ if (standardFormat == default)
+ {
+ return TryParseSByteD(source, out value, out bytesConsumed);
+ }
+
+ // There's small but measurable overhead when entering the switch block below.
+ // We optimize for the default case by hoisting it above the switch block.
+
+ switch (standardFormat | 0x20) // convert to lowercase
{
- case default(char):
case 'g':
- case 'G':
case 'd':
- case 'D':
- return TryParseSByteD(source, out value, out bytesConsumed);
+ standardFormat = default;
+ goto FastPath;
case 'n':
- case 'N':
return TryParseSByteN(source, out value, out bytesConsumed);
case 'x':
- case 'X':
- value = default;
+ Unsafe.SkipInit(out value); // will be populated by TryParseByteX
return TryParseByteX(source, out Unsafe.As(ref value), out bytesConsumed);
default:
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(source, out value, out bytesConsumed);
}
}
@@ -81,26 +86,31 @@ namespace System.Buffers.Text
///
public static bool TryParse(ReadOnlySpan source, out short value, out int bytesConsumed, char standardFormat = default)
{
- switch (standardFormat)
+ FastPath:
+ if (standardFormat == default)
+ {
+ return TryParseInt16D(source, out value, out bytesConsumed);
+ }
+
+ // There's small but measurable overhead when entering the switch block below.
+ // We optimize for the default case by hoisting it above the switch block.
+
+ switch (standardFormat | 0x20) // convert to lowercase
{
- case default(char):
case 'g':
- case 'G':
case 'd':
- case 'D':
- return TryParseInt16D(source, out value, out bytesConsumed);
+ standardFormat = default;
+ goto FastPath;
case 'n':
- case 'N':
return TryParseInt16N(source, out value, out bytesConsumed);
case 'x':
- case 'X':
- value = default;
+ Unsafe.SkipInit(out value); // will be populated by TryParseUInt16X
return TryParseUInt16X(source, out Unsafe.As(ref value), out bytesConsumed);
default:
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(source, out value, out bytesConsumed);
}
}
@@ -127,26 +137,31 @@ namespace System.Buffers.Text
///
public static bool TryParse(ReadOnlySpan source, out int value, out int bytesConsumed, char standardFormat = default)
{
- switch (standardFormat)
+ FastPath:
+ if (standardFormat == default)
+ {
+ return TryParseInt32D(source, out value, out bytesConsumed);
+ }
+
+ // There's small but measurable overhead when entering the switch block below.
+ // We optimize for the default case by hoisting it above the switch block.
+
+ switch (standardFormat | 0x20) // convert to lowercase
{
- case default(char):
case 'g':
- case 'G':
case 'd':
- case 'D':
- return TryParseInt32D(source, out value, out bytesConsumed);
+ standardFormat = default;
+ goto FastPath;
case 'n':
- case 'N':
return TryParseInt32N(source, out value, out bytesConsumed);
case 'x':
- case 'X':
- value = default;
+ Unsafe.SkipInit(out value); // will be populated by TryParseUInt32X
return TryParseUInt32X(source, out Unsafe.As(ref value), out bytesConsumed);
default:
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(source, out value, out bytesConsumed);
}
}
@@ -173,26 +188,31 @@ namespace System.Buffers.Text
///
public static bool TryParse(ReadOnlySpan source, out long value, out int bytesConsumed, char standardFormat = default)
{
- switch (standardFormat)
+ FastPath:
+ if (standardFormat == default)
+ {
+ return TryParseInt64D(source, out value, out bytesConsumed);
+ }
+
+ // There's small but measurable overhead when entering the switch block below.
+ // We optimize for the default case by hoisting it above the switch block.
+
+ switch (standardFormat | 0x20) // convert to lowercase
{
- case default(char):
case 'g':
- case 'G':
case 'd':
- case 'D':
- return TryParseInt64D(source, out value, out bytesConsumed);
+ standardFormat = default;
+ goto FastPath;
case 'n':
- case 'N':
return TryParseInt64N(source, out value, out bytesConsumed);
case 'x':
- case 'X':
- value = default;
+ Unsafe.SkipInit(out value); // will be populated by TryParseUInt64X
return TryParseUInt64X(source, out Unsafe.As(ref value), out bytesConsumed);
default:
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(source, out value, out bytesConsumed);
}
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.cs b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.cs
index 476889c1f..5d3237f19 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.cs
@@ -29,25 +29,30 @@ namespace System.Buffers.Text
///
public static bool TryParse(ReadOnlySpan source, out byte value, out int bytesConsumed, char standardFormat = default)
{
- switch (standardFormat)
+ FastPath:
+ if (standardFormat == default)
+ {
+ return TryParseByteD(source, out value, out bytesConsumed);
+ }
+
+ // There's small but measurable overhead when entering the switch block below.
+ // We optimize for the default case by hoisting it above the switch block.
+
+ switch (standardFormat | 0x20) // convert to lowercase
{
- case default(char):
case 'g':
- case 'G':
case 'd':
- case 'D':
- return TryParseByteD(source, out value, out bytesConsumed);
+ standardFormat = default;
+ goto FastPath;
case 'n':
- case 'N':
return TryParseByteN(source, out value, out bytesConsumed);
case 'x':
- case 'X':
return TryParseByteX(source, out value, out bytesConsumed);
default:
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(source, out value, out bytesConsumed);
}
}
@@ -75,25 +80,30 @@ namespace System.Buffers.Text
[CLSCompliant(false)]
public static bool TryParse(ReadOnlySpan source, out ushort value, out int bytesConsumed, char standardFormat = default)
{
- switch (standardFormat)
+ FastPath:
+ if (standardFormat == default)
+ {
+ return TryParseUInt16D(source, out value, out bytesConsumed);
+ }
+
+ // There's small but measurable overhead when entering the switch block below.
+ // We optimize for the default case by hoisting it above the switch block.
+
+ switch (standardFormat | 0x20) // convert to lowercase
{
- case default(char):
case 'g':
- case 'G':
case 'd':
- case 'D':
- return TryParseUInt16D(source, out value, out bytesConsumed);
+ standardFormat = default;
+ goto FastPath;
case 'n':
- case 'N':
return TryParseUInt16N(source, out value, out bytesConsumed);
case 'x':
- case 'X':
return TryParseUInt16X(source, out value, out bytesConsumed);
default:
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(source, out value, out bytesConsumed);
}
}
@@ -121,25 +131,30 @@ namespace System.Buffers.Text
[CLSCompliant(false)]
public static bool TryParse(ReadOnlySpan source, out uint value, out int bytesConsumed, char standardFormat = default)
{
- switch (standardFormat)
+ FastPath:
+ if (standardFormat == default)
+ {
+ return TryParseUInt32D(source, out value, out bytesConsumed);
+ }
+
+ // There's small but measurable overhead when entering the switch block below.
+ // We optimize for the default case by hoisting it above the switch block.
+
+ switch (standardFormat | 0x20) // convert to lowercase
{
- case default(char):
case 'g':
- case 'G':
case 'd':
- case 'D':
- return TryParseUInt32D(source, out value, out bytesConsumed);
+ standardFormat = default;
+ goto FastPath;
case 'n':
- case 'N':
return TryParseUInt32N(source, out value, out bytesConsumed);
case 'x':
- case 'X':
return TryParseUInt32X(source, out value, out bytesConsumed);
default:
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(source, out value, out bytesConsumed);
}
}
@@ -167,25 +182,30 @@ namespace System.Buffers.Text
[CLSCompliant(false)]
public static bool TryParse(ReadOnlySpan source, out ulong value, out int bytesConsumed, char standardFormat = default)
{
- switch (standardFormat)
+ FastPath:
+ if (standardFormat == default)
+ {
+ return TryParseUInt64D(source, out value, out bytesConsumed);
+ }
+
+ // There's small but measurable overhead when entering the switch block below.
+ // We optimize for the default case by hoisting it above the switch block.
+
+ switch (standardFormat | 0x20) // convert to lowercase
{
- case default(char):
case 'g':
- case 'G':
case 'd':
- case 'D':
- return TryParseUInt64D(source, out value, out bytesConsumed);
+ standardFormat = default;
+ goto FastPath;
case 'n':
- case 'N':
return TryParseUInt64N(source, out value, out bytesConsumed);
case 'x':
- case 'X':
return TryParseUInt64X(source, out value, out bytesConsumed);
default:
- return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(source, out value, out bytesConsumed);
}
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Byte.cs b/src/System.Private.CoreLib/shared/System/Byte.cs
index 88d349a4b..c58709748 100644
--- a/src/System.Private.CoreLib/shared/System/Byte.cs
+++ b/src/System.Private.CoreLib/shared/System/Byte.cs
@@ -2,6 +2,7 @@
// 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.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -117,7 +118,7 @@ namespace System
return (byte)i;
}
- public static bool TryParse(string? s, out byte result)
+ public static bool TryParse([NotNullWhen(true)] string? s, out byte result)
{
if (s == null)
{
@@ -133,7 +134,7 @@ namespace System
return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
- public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out byte result)
+ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out byte result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
@@ -166,7 +167,7 @@ namespace System
public override string ToString()
{
- return Number.UInt32ToDecStr(m_value, -1);
+ return Number.UInt32ToDecStr(m_value);
}
public string ToString(string? format)
@@ -176,7 +177,7 @@ namespace System
public string ToString(IFormatProvider? provider)
{
- return Number.FormatUInt32(m_value, null, provider);
+ return Number.UInt32ToDecStr(m_value);
}
public string ToString(string? format, IFormatProvider? provider)
diff --git a/src/System.Private.CoreLib/shared/System/Char.cs b/src/System.Private.CoreLib/shared/System/Char.cs
index 02e49c904..2ccf35ee7 100644
--- a/src/System.Private.CoreLib/shared/System/Char.cs
+++ b/src/System.Private.CoreLib/shared/System/Char.cs
@@ -13,6 +13,7 @@
===========================================================*/
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
@@ -182,7 +183,7 @@ namespace System
return s[0];
}
- public static bool TryParse(string? s, out char result)
+ public static bool TryParse([NotNullWhen(true)] string? s, out char result)
{
result = '\0';
if (s == null)
@@ -353,10 +354,7 @@ namespace System
}
// Converts a character to upper-case for invariant culture.
- public static char ToUpperInvariant(char c)
- {
- return TextInfo.Invariant.ToUpper(c);
- }
+ public static char ToUpperInvariant(char c) => TextInfo.ToUpperInvariant(c);
/*===================================ToLower====================================
**
@@ -382,10 +380,7 @@ namespace System
}
// Converts a character to lower-case for invariant culture.
- public static char ToLowerInvariant(char c)
- {
- return TextInfo.Invariant.ToLower(c);
- }
+ public static char ToLowerInvariant(char c) => TextInfo.ToLowerInvariant(c);
//
// IConvertible implementation
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Concurrent/ConcurrentQueue.cs b/src/System.Private.CoreLib/shared/System/Collections/Concurrent/ConcurrentQueue.cs
index ae86c6f96..480890eca 100644
--- a/src/System.Private.CoreLib/shared/System/Collections/Concurrent/ConcurrentQueue.cs
+++ b/src/System.Private.CoreLib/shared/System/Collections/Concurrent/ConcurrentQueue.cs
@@ -222,9 +222,7 @@ namespace System.Collections.Concurrent
public T[] ToArray()
{
// Snap the current contents for enumeration.
- ConcurrentQueueSegment head, tail;
- int headHead, tailTail;
- SnapForObservation(out head, out headHead, out tail, out tailTail);
+ SnapForObservation(out ConcurrentQueueSegment head, out int headHead, out ConcurrentQueueSegment tail, out int tailTail);
// Count the number of items in that snapped set, and use it to allocate an
// array of the right size.
@@ -450,9 +448,7 @@ namespace System.Collections.Concurrent
}
// Snap for enumeration
- ConcurrentQueueSegment head, tail;
- int headHead, tailTail;
- SnapForObservation(out head, out headHead, out tail, out tailTail);
+ SnapForObservation(out ConcurrentQueueSegment head, out int headHead, out ConcurrentQueueSegment tail, out int tailTail);
// Get the number of items to be enumerated
long count = GetCount(head, headHead, tail, tailTail);
@@ -484,9 +480,7 @@ namespace System.Collections.Concurrent
///
public IEnumerator GetEnumerator()
{
- ConcurrentQueueSegment head, tail;
- int headHead, tailTail;
- SnapForObservation(out head, out headHead, out tail, out tailTail);
+ SnapForObservation(out ConcurrentQueueSegment head, out int headHead, out ConcurrentQueueSegment tail, out int tailTail);
return Enumerate(head, headHead, tail, tailTail);
}
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/ArraySortHelper.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/ArraySortHelper.cs
index 46b9890df..c0da64353 100644
--- a/src/System.Private.CoreLib/shared/System/Collections/Generic/ArraySortHelper.cs
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/ArraySortHelper.cs
@@ -2,50 +2,23 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-/*============================================================
-**
-**
-**
-**
-**
-** Purpose: class to sort arrays
-**
-**
-===========================================================*/
-
using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
+using System.Numerics;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Internal.Runtime.CompilerServices;
+
+#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
+#if TARGET_64BIT
+using nint = System.Int64;
+#else
+using nint = System.Int32;
+#endif
namespace System.Collections.Generic
{
#region ArraySortHelper for single arrays
- internal static class IntrospectiveSortUtilities
- {
- // This is the threshold where Introspective sort switches to Insertion sort.
- // Empirically, 16 seems to speed up most cases without slowing down others, at least for integers.
- // Large value types may benefit from a smaller number.
- internal const int IntrosortSizeThreshold = 16;
-
- internal static int FloorLog2PlusOne(int n)
- {
- int result = 0;
- while (n >= 1)
- {
- result++;
- n /= 2;
- }
- return result;
- }
-
- [DoesNotReturn]
- internal static void ThrowOrIgnoreBadComparer(object? comparer)
- {
- throw new ArgumentException(SR.Format(SR.Arg_BogusIComparer, comparer));
- }
- }
-
internal partial class ArraySortHelper
{
#region IArraySortHelper Members
@@ -61,11 +34,11 @@ namespace System.Collections.Generic
}
catch (IndexOutOfRangeException)
{
- IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ ThrowHelper.ThrowArgumentException_BadComparer(comparer);
}
catch (Exception e)
{
- throw new InvalidOperationException(SR.InvalidOperation_IComparerFailed, e);
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
}
}
@@ -78,7 +51,8 @@ namespace System.Collections.Generic
}
catch (Exception e)
{
- throw new InvalidOperationException(SR.InvalidOperation_IComparerFailed, e);
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
+ return 0;
}
}
@@ -95,11 +69,11 @@ namespace System.Collections.Generic
}
catch (IndexOutOfRangeException)
{
- IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ ThrowHelper.ThrowArgumentException_BadComparer(comparer);
}
catch (Exception e)
{
- throw new InvalidOperationException(SR.InvalidOperation_IComparerFailed, e);
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
}
}
@@ -131,26 +105,24 @@ namespace System.Collections.Generic
private static void SwapIfGreater(Span keys, Comparison comparer, int i, int j)
{
- if (i != j)
+ Debug.Assert(i != j);
+
+ if (comparer(keys[i], keys[j]) > 0)
{
- if (comparer(keys[i], keys[j]) > 0)
- {
- T key = keys[i];
- keys[i] = keys[j];
- keys[j] = key;
- }
+ T key = keys[i];
+ keys[i] = keys[j];
+ keys[j] = key;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(Span a, int i, int j)
{
- if (i != j)
- {
- T t = a[i];
- a[i] = a[j];
- a[j] = t;
- }
+ Debug.Assert(i != j);
+
+ T t = a[i];
+ a[i] = a[j];
+ a[j] = t;
}
internal static void IntrospectiveSort(Span keys, Comparison comparer)
@@ -159,53 +131,51 @@ namespace System.Collections.Generic
if (keys.Length > 1)
{
- IntroSort(keys, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length), comparer);
+ IntroSort(keys, 2 * (BitOperations.Log2((uint)keys.Length) + 1), comparer);
}
}
private static void IntroSort(Span keys, int depthLimit, Comparison comparer)
{
- int lo = 0;
- int hi = keys.Length - 1;
-
+ Debug.Assert(!keys.IsEmpty);
+ Debug.Assert(depthLimit >= 0);
Debug.Assert(comparer != null);
- while (hi > lo)
+ int hi = keys.Length - 1;
+ while (hi > 0)
{
- int partitionSize = hi - lo + 1;
- if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ int partitionSize = hi + 1;
+
+ if (partitionSize <= Array.IntrosortSizeThreshold)
{
- if (partitionSize == 1)
- {
- return;
- }
+ Debug.Assert(partitionSize >= 2);
if (partitionSize == 2)
{
- SwapIfGreater(keys, comparer, lo, hi);
+ SwapIfGreater(keys, comparer, 0, hi);
return;
}
if (partitionSize == 3)
{
- SwapIfGreater(keys, comparer, lo, hi - 1);
- SwapIfGreater(keys, comparer, lo, hi);
+ SwapIfGreater(keys, comparer, 0, hi - 1);
+ SwapIfGreater(keys, comparer, 0, hi);
SwapIfGreater(keys, comparer, hi - 1, hi);
return;
}
- InsertionSort(keys[lo..(hi+1)], comparer);
+ InsertionSort(keys.Slice(0, hi+1), comparer);
return;
}
if (depthLimit == 0)
{
- HeapSort(keys[lo..(hi+1)], comparer);
+ HeapSort(keys.Slice(0, hi+1), comparer);
return;
}
depthLimit--;
- int p = PickPivotAndPartition(keys[lo..(hi+1)], comparer);
+ int p = PickPivotAndPartition(keys.Slice(0, hi+1), comparer);
// Note we've already partitioned around the pivot and do not have to move the pivot again.
IntroSort(keys[(p+1)..(hi+1)], depthLimit, comparer);
@@ -215,23 +185,22 @@ namespace System.Collections.Generic
private static int PickPivotAndPartition(Span keys, Comparison comparer)
{
+ Debug.Assert(keys.Length >= Array.IntrosortSizeThreshold);
Debug.Assert(comparer != null);
- Debug.Assert(!keys.IsEmpty);
- int lo = 0;
int hi = keys.Length - 1;
// Compute median-of-three. But also partition them, since we've done the comparison.
- int middle = lo + ((hi - lo) / 2);
+ int middle = hi >> 1;
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
- SwapIfGreater(keys, comparer, lo, middle); // swap the low with the mid point
- SwapIfGreater(keys, comparer, lo, hi); // swap the low with the high
+ SwapIfGreater(keys, comparer, 0, middle); // swap the low with the mid point
+ SwapIfGreater(keys, comparer, 0, hi); // swap the low with the high
SwapIfGreater(keys, comparer, middle, hi); // swap the middle with the high
T pivot = keys[middle];
Swap(keys, middle, hi - 1);
- int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
+ int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
while (left < right)
{
@@ -245,7 +214,10 @@ namespace System.Collections.Generic
}
// Put pivot in the right location.
- Swap(keys, left, hi - 1);
+ if (left != hi - 1)
+ {
+ Swap(keys, left, hi - 1);
+ }
return left;
}
@@ -254,20 +226,16 @@ namespace System.Collections.Generic
Debug.Assert(comparer != null);
Debug.Assert(!keys.IsEmpty);
- int lo = 0;
- int hi = keys.Length - 1;
-
- int n = hi - lo + 1;
-
- for (int i = n / 2; i >= 1; i--)
+ int n = keys.Length;
+ for (int i = n >> 1; i >= 1; i--)
{
- DownHeap(keys, i, n, lo, comparer);
+ DownHeap(keys, i, n, 0, comparer);
}
for (int i = n; i > 1; i--)
{
- Swap(keys, lo, lo + i - 1);
- DownHeap(keys, 1, i - 1, lo, comparer);
+ Swap(keys, 0, i - 1);
+ DownHeap(keys, 1, i - 1, 0, comparer);
}
}
@@ -278,7 +246,7 @@ namespace System.Collections.Generic
Debug.Assert(lo < keys.Length);
T d = keys[lo + i - 1];
- while (i <= n / 2)
+ while (i <= n >> 1)
{
int child = 2 * i;
if (child < n && comparer(keys[lo + child - 1], keys[lo + child]) < 0)
@@ -327,7 +295,10 @@ namespace System.Collections.Generic
{
if (comparer == null || comparer == Comparer.Default)
{
- IntrospectiveSort(keys);
+ if (keys.Length > 1)
+ {
+ IntroSort(keys, 2 * (BitOperations.Log2((uint)keys.Length) + 1));
+ }
}
else
{
@@ -336,11 +307,11 @@ namespace System.Collections.Generic
}
catch (IndexOutOfRangeException)
{
- IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ ThrowHelper.ThrowArgumentException_BadComparer(comparer);
}
catch (Exception e)
{
- throw new InvalidOperationException(SR.InvalidOperation_IComparerFailed, e);
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
}
}
@@ -362,7 +333,8 @@ namespace System.Collections.Generic
}
catch (Exception e)
{
- throw new InvalidOperationException(SR.InvalidOperation_IComparerFailed, e);
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
+ return 0;
}
}
@@ -406,147 +378,140 @@ namespace System.Collections.Generic
return ~lo;
}
- private static void SwapIfGreater(Span keys, int i, int j)
- {
- Debug.Assert(0 <= i && i < keys.Length);
- Debug.Assert(0 <= j && j < keys.Length);
-
- if (i != j)
- {
- if (keys[i] != null && keys[i].CompareTo(keys[j]) > 0)
- {
- T key = keys[i];
- keys[i] = keys[j];
- keys[j] = key;
- }
- }
- }
-
+ /// Swaps the values in the two references if the first is greater than the second.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void Swap(Span a, int i, int j)
+ private static void SwapIfGreater(ref T i, ref T j)
{
- if (i != j)
+ if (i != null && i.CompareTo(j) > 0)
{
- T t = a[i];
- a[i] = a[j];
- a[j] = t;
+ Swap(ref i, ref j);
}
}
- internal static void IntrospectiveSort(Span keys)
+ /// Swaps the values in the two references, regardless of whether the two references are the same.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Swap(ref T i, ref T j)
{
- if (keys.Length > 1)
- {
- IntroSort(keys, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length));
- }
+ Debug.Assert(!Unsafe.AreSame(ref i, ref j));
+
+ T t = i;
+ i = j;
+ j = t;
}
private static void IntroSort(Span keys, int depthLimit)
{
- int lo = 0;
- int hi = keys.Length - 1;
+ Debug.Assert(!keys.IsEmpty);
+ Debug.Assert(depthLimit >= 0);
- while (hi > lo)
+ int hi = keys.Length - 1;
+ while (hi > 0)
{
- int partitionSize = hi - lo + 1;
- if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ int partitionSize = hi + 1;
+
+ if (partitionSize <= Array.IntrosortSizeThreshold)
{
- if (partitionSize == 1)
- {
- return;
- }
+ Debug.Assert(partitionSize >= 2);
+
if (partitionSize == 2)
{
- SwapIfGreater(keys, lo, hi);
- return;
- }
- if (partitionSize == 3)
- {
- SwapIfGreater(keys, lo, hi - 1);
- SwapIfGreater(keys, lo, hi);
- SwapIfGreater(keys, hi - 1, hi);
+ SwapIfGreater(ref keys[0], ref keys[1]);
return;
}
- InsertionSort(keys[lo..(hi+1)]);
+ if (partitionSize == 3)
+ {
+ ref T hiRef = ref keys[2];
+ ref T him1Ref = ref keys[1];
+ ref T loRef = ref keys[0];
+
+ SwapIfGreater(ref loRef, ref him1Ref);
+ SwapIfGreater(ref loRef, ref hiRef);
+ SwapIfGreater(ref him1Ref, ref hiRef);
+ return;
+ }
+
+ InsertionSort(keys.Slice(0, partitionSize));
return;
}
if (depthLimit == 0)
{
- HeapSort(keys[lo..(hi+1)]);
+ HeapSort(keys.Slice(0, partitionSize));
return;
}
depthLimit--;
- int p = PickPivotAndPartition(keys[lo..(hi+1)]);
+ int p = PickPivotAndPartition(keys.Slice(0, partitionSize));
// Note we've already partitioned around the pivot and do not have to move the pivot again.
- IntroSort(keys[(p+1)..(hi+1)], depthLimit);
+ IntroSort(keys[(p+1)..partitionSize], depthLimit);
hi = p - 1;
}
}
private static int PickPivotAndPartition(Span keys)
{
- Debug.Assert(!keys.IsEmpty);
+ Debug.Assert(keys.Length >= Array.IntrosortSizeThreshold);
- int lo = 0;
- int hi = keys.Length - 1;
+ // Use median-of-three to select a pivot. Grab a reference to the 0th, Length-1th, and Length/2th elements, and sort them.
+ ref T zeroRef = ref MemoryMarshal.GetReference(keys);
+ ref T lastRef = ref Unsafe.Add(ref zeroRef, keys.Length - 1);
+ ref T middleRef = ref Unsafe.Add(ref zeroRef, (keys.Length - 1) >> 1);
+ SwapIfGreater(ref zeroRef, ref middleRef);
+ SwapIfGreater(ref zeroRef, ref lastRef);
+ SwapIfGreater(ref middleRef, ref lastRef);
- // Compute median-of-three. But also partition them, since we've done the comparison.
- int middle = lo + ((hi - lo) / 2);
+ // Select the middle value as the pivot, and move it to be just before the last element.
+ ref T nextToLastRef = ref Unsafe.Add(ref zeroRef, keys.Length - 2);
+ T pivot = middleRef;
+ Swap(ref middleRef, ref nextToLastRef);
- // Sort lo, mid and hi appropriately, then pick mid as the pivot.
- SwapIfGreater(keys, lo, middle); // swap the low with the mid point
- SwapIfGreater(keys, lo, hi); // swap the low with the high
- SwapIfGreater(keys, middle, hi); // swap the middle with the high
-
- T pivot = keys[middle];
- Swap(keys, middle, hi - 1);
- int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
-
- while (left < right)
+ // Walk the left and right pointers, swapping elements as necessary, until they cross.
+ ref T leftRef = ref zeroRef, rightRef = ref nextToLastRef;
+ while (Unsafe.IsAddressLessThan(ref leftRef, ref rightRef))
{
if (pivot == null)
{
- while (left < (hi - 1) && keys[++left] == null) ;
- while (right > lo && keys[--right] != null) ;
+ while (Unsafe.IsAddressLessThan(ref leftRef, ref nextToLastRef) && (leftRef = ref Unsafe.Add(ref leftRef, 1)) == null) ;
+ while (Unsafe.IsAddressGreaterThan(ref rightRef, ref zeroRef) && (rightRef = ref Unsafe.Add(ref rightRef, -1)) == null) ;
}
else
{
- while (pivot.CompareTo(keys[++left]) > 0) ;
- while (pivot.CompareTo(keys[--right]) < 0) ;
+ while (Unsafe.IsAddressLessThan(ref leftRef, ref nextToLastRef) && pivot.CompareTo(leftRef = ref Unsafe.Add(ref leftRef, 1)) > 0) ;
+ while (Unsafe.IsAddressGreaterThan(ref rightRef, ref zeroRef) && pivot.CompareTo(rightRef = ref Unsafe.Add(ref rightRef, -1)) < 0) ;
}
- if (left >= right)
+ if (!Unsafe.IsAddressLessThan(ref leftRef, ref rightRef))
+ {
break;
+ }
- Swap(keys, left, right);
+ Swap(ref leftRef, ref rightRef);
}
- // Put pivot in the right location.
- Swap(keys, left, hi - 1);
- return left;
+ // Put the pivot in the correct location.
+ if (!Unsafe.AreSame(ref leftRef, ref nextToLastRef))
+ {
+ Swap(ref leftRef, ref nextToLastRef);
+ }
+ return (int)((nint)Unsafe.ByteOffset(ref zeroRef, ref leftRef) / Unsafe.SizeOf());
}
private static void HeapSort(Span keys)
{
Debug.Assert(!keys.IsEmpty);
- int lo = 0;
- int hi = keys.Length - 1;
-
- int n = hi - lo + 1;
- for (int i = n / 2; i >= 1; i = i - 1)
+ int n = keys.Length;
+ for (int i = n >> 1; i >= 1; i--)
{
- DownHeap(keys, i, n, lo);
+ DownHeap(keys, i, n, 0);
}
for (int i = n; i > 1; i--)
{
- Swap(keys, lo, lo + i - 1);
- DownHeap(keys, 1, i - 1, lo);
+ Swap(ref keys[0], ref keys[i - 1]);
+ DownHeap(keys, 1, i - 1, 0);
}
}
@@ -556,7 +521,7 @@ namespace System.Collections.Generic
Debug.Assert(lo < keys.Length);
T d = keys[lo + i - 1];
- while (i <= n / 2)
+ while (i <= n >> 1)
{
int child = 2 * i;
if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
@@ -578,16 +543,16 @@ namespace System.Collections.Generic
{
for (int i = 0; i < keys.Length - 1; i++)
{
- T t = keys[i + 1];
+ T t = Unsafe.Add(ref MemoryMarshal.GetReference(keys), i + 1);
int j = i;
- while (j >= 0 && (t == null || t.CompareTo(keys[j]) < 0))
+ while (j >= 0 && (t == null || t.CompareTo(Unsafe.Add(ref MemoryMarshal.GetReference(keys), j)) < 0))
{
- keys[j + 1] = keys[j];
+ Unsafe.Add(ref MemoryMarshal.GetReference(keys), j + 1) = Unsafe.Add(ref MemoryMarshal.GetReference(keys), j);
j--;
}
- keys[j + 1] = t;
+ Unsafe.Add(ref MemoryMarshal.GetReference(keys), j + 1) = t;
}
}
}
@@ -608,11 +573,11 @@ namespace System.Collections.Generic
}
catch (IndexOutOfRangeException)
{
- IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ ThrowHelper.ThrowArgumentException_BadComparer(comparer);
}
catch (Exception e)
{
- throw new InvalidOperationException(SR.InvalidOperation_IComparerFailed, e);
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
}
}
@@ -621,35 +586,32 @@ namespace System.Collections.Generic
Debug.Assert(comparer != null);
Debug.Assert(0 <= i && i < keys.Length && i < values.Length);
Debug.Assert(0 <= j && j < keys.Length && j < values.Length);
+ Debug.Assert(i != j);
- if (i != j)
+ if (comparer.Compare(keys[i], keys[j]) > 0)
{
- if (comparer.Compare(keys[i], keys[j]) > 0)
- {
- TKey key = keys[i];
- keys[i] = keys[j];
- keys[j] = key;
+ TKey key = keys[i];
+ keys[i] = keys[j];
+ keys[j] = key;
- TValue value = values[i];
- values[i] = values[j];
- values[j] = value;
- }
+ TValue value = values[i];
+ values[i] = values[j];
+ values[j] = value;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(Span keys, Span values, int i, int j)
{
- if (i != j)
- {
- TKey k = keys[i];
- keys[i] = keys[j];
- keys[j] = k;
+ Debug.Assert(i != j);
- TValue v = values[i];
- values[i] = values[j];
- values[j] = v;
- }
+ TKey k = keys[i];
+ keys[i] = keys[j];
+ keys[j] = k;
+
+ TValue v = values[i];
+ values[i] = values[j];
+ values[j] = v;
}
internal static void IntrospectiveSort(Span keys, Span values, IComparer comparer)
@@ -659,79 +621,77 @@ namespace System.Collections.Generic
if (keys.Length > 1)
{
- IntroSort(keys, values, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length), comparer);
+ IntroSort(keys, values, 2 * (BitOperations.Log2((uint)keys.Length) + 1), comparer);
}
}
private static void IntroSort(Span keys, Span values, int depthLimit, IComparer comparer)
{
+ Debug.Assert(!keys.IsEmpty);
+ Debug.Assert(values.Length == keys.Length);
+ Debug.Assert(depthLimit >= 0);
Debug.Assert(comparer != null);
- int lo = 0;
int hi = keys.Length - 1;
-
- while (hi > lo)
+ while (hi > 0)
{
- int partitionSize = hi - lo + 1;
- if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ int partitionSize = hi + 1;
+
+ if (partitionSize <= Array.IntrosortSizeThreshold)
{
- if (partitionSize == 1)
- {
- return;
- }
+ Debug.Assert(partitionSize >= 2);
if (partitionSize == 2)
{
- SwapIfGreaterWithValues(keys, values, comparer, lo, hi);
+ SwapIfGreaterWithValues(keys, values, comparer, 0, hi);
return;
}
if (partitionSize == 3)
{
- SwapIfGreaterWithValues(keys, values, comparer, lo, hi - 1);
- SwapIfGreaterWithValues(keys, values, comparer, lo, hi);
+ SwapIfGreaterWithValues(keys, values, comparer, 0, hi - 1);
+ SwapIfGreaterWithValues(keys, values, comparer, 0, hi);
SwapIfGreaterWithValues(keys, values, comparer, hi - 1, hi);
return;
}
- InsertionSort(keys[lo..(hi+1)], values[lo..(hi+1)], comparer);
+ InsertionSort(keys.Slice(0, partitionSize), values.Slice(0, partitionSize), comparer);
return;
}
if (depthLimit == 0)
{
- HeapSort(keys[lo..(hi+1)], values[lo..(hi+1)], comparer);
+ HeapSort(keys.Slice(0, partitionSize), values.Slice(0, partitionSize), comparer);
return;
}
depthLimit--;
- int p = PickPivotAndPartition(keys[lo..(hi+1)], values[lo..(hi+1)], comparer);
+ int p = PickPivotAndPartition(keys.Slice(0, partitionSize), values.Slice(0, partitionSize), comparer);
// Note we've already partitioned around the pivot and do not have to move the pivot again.
- IntroSort(keys[(p+1)..(hi+1)], values[(p+1)..(hi+1)], depthLimit, comparer);
+ IntroSort(keys[(p+1)..partitionSize], values[(p+1)..partitionSize], depthLimit, comparer);
hi = p - 1;
}
}
private static int PickPivotAndPartition(Span keys, Span values, IComparer comparer)
{
+ Debug.Assert(keys.Length >= Array.IntrosortSizeThreshold);
Debug.Assert(comparer != null);
- Debug.Assert(!keys.IsEmpty);
- int lo = 0;
int hi = keys.Length - 1;
// Compute median-of-three. But also partition them, since we've done the comparison.
- int middle = lo + ((hi - lo) / 2);
+ int middle = hi >> 1;
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
- SwapIfGreaterWithValues(keys, values, comparer, lo, middle); // swap the low with the mid point
- SwapIfGreaterWithValues(keys, values, comparer, lo, hi); // swap the low with the high
+ SwapIfGreaterWithValues(keys, values, comparer, 0, middle); // swap the low with the mid point
+ SwapIfGreaterWithValues(keys, values, comparer, 0, hi); // swap the low with the high
SwapIfGreaterWithValues(keys, values, comparer, middle, hi); // swap the middle with the high
TKey pivot = keys[middle];
Swap(keys, values, middle, hi - 1);
- int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
+ int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
while (left < right)
{
@@ -745,7 +705,10 @@ namespace System.Collections.Generic
}
// Put pivot in the right location.
- Swap(keys, values, left, hi - 1);
+ if (left != hi - 1)
+ {
+ Swap(keys, values, left, hi - 1);
+ }
return left;
}
@@ -754,19 +717,16 @@ namespace System.Collections.Generic
Debug.Assert(comparer != null);
Debug.Assert(!keys.IsEmpty);
- int lo = 0;
- int hi = keys.Length - 1;
-
- int n = hi - lo + 1;
- for (int i = n / 2; i >= 1; i--)
+ int n = keys.Length;
+ for (int i = n >> 1; i >= 1; i--)
{
- DownHeap(keys, values, i, n, lo, comparer);
+ DownHeap(keys, values, i, n, 0, comparer);
}
for (int i = n; i > 1; i--)
{
- Swap(keys, values, lo, lo + i - 1);
- DownHeap(keys, values, 1, i - 1, lo, comparer);
+ Swap(keys, values, 0, i - 1);
+ DownHeap(keys, values, 1, i - 1, 0, comparer);
}
}
@@ -779,7 +739,7 @@ namespace System.Collections.Generic
TKey d = keys[lo + i - 1];
TValue dValue = values[lo + i - 1];
- while (i <= n / 2)
+ while (i <= n >> 1)
{
int child = 2 * i;
if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
@@ -842,44 +802,42 @@ namespace System.Collections.Generic
}
catch (IndexOutOfRangeException)
{
- IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
+ ThrowHelper.ThrowArgumentException_BadComparer(comparer);
}
catch (Exception e)
{
- throw new InvalidOperationException(SR.InvalidOperation_IComparerFailed, e);
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
}
}
private static void SwapIfGreaterWithValues(Span keys, Span values, int i, int j)
{
- if (i != j)
- {
- if (keys[i] != null && keys[i].CompareTo(keys[j]) > 0)
- {
- TKey key = keys[i];
- keys[i] = keys[j];
- keys[j] = key;
+ Debug.Assert(i != j);
- TValue value = values[i];
- values[i] = values[j];
- values[j] = value;
- }
+ if (keys[i] != null && keys[i].CompareTo(keys[j]) > 0)
+ {
+ TKey key = keys[i];
+ keys[i] = keys[j];
+ keys[j] = key;
+
+ TValue value = values[i];
+ values[i] = values[j];
+ values[j] = value;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(Span keys, Span values, int i, int j)
{
- if (i != j)
- {
- TKey k = keys[i];
- keys[i] = keys[j];
- keys[j] = k;
+ Debug.Assert(i != j);
- TValue v = values[i];
- values[i] = values[j];
- values[j] = v;
- }
+ TKey k = keys[i];
+ keys[i] = keys[j];
+ keys[j] = k;
+
+ TValue v = values[i];
+ values[i] = values[j];
+ values[j] = v;
}
internal static void IntrospectiveSort(Span keys, Span values)
@@ -888,83 +846,82 @@ namespace System.Collections.Generic
if (keys.Length > 1)
{
- IntroSort(keys, values, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length));
+ IntroSort(keys, values, 2 * (BitOperations.Log2((uint)keys.Length) + 1));
}
}
private static void IntroSort(Span keys, Span values, int depthLimit)
{
- int lo = 0;
- int hi = keys.Length - 1;
+ Debug.Assert(!keys.IsEmpty);
+ Debug.Assert(values.Length == keys.Length);
+ Debug.Assert(depthLimit >= 0);
- while (hi > lo)
+ int hi = keys.Length - 1;
+ while (hi > 0)
{
- int partitionSize = hi - lo + 1;
- if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
+ int partitionSize = hi + 1;
+
+ if (partitionSize <= Array.IntrosortSizeThreshold)
{
- if (partitionSize == 1)
- {
- return;
- }
+ Debug.Assert(partitionSize >= 2);
if (partitionSize == 2)
{
- SwapIfGreaterWithValues(keys, values, lo, hi);
+ SwapIfGreaterWithValues(keys, values, 0, hi);
return;
}
if (partitionSize == 3)
{
- SwapIfGreaterWithValues(keys, values, lo, hi - 1);
- SwapIfGreaterWithValues(keys, values, lo, hi);
+ SwapIfGreaterWithValues(keys, values, 0, hi - 1);
+ SwapIfGreaterWithValues(keys, values, 0, hi);
SwapIfGreaterWithValues(keys, values, hi - 1, hi);
return;
}
- InsertionSort(keys[lo..(hi+1)], values[lo..(hi+1)]);
+ InsertionSort(keys.Slice(0, partitionSize), values.Slice(0, partitionSize));
return;
}
if (depthLimit == 0)
{
- HeapSort(keys[lo..(hi+1)], values[lo..(hi+1)]);
+ HeapSort(keys.Slice(0, partitionSize), values.Slice(0, partitionSize));
return;
}
depthLimit--;
- int p = PickPivotAndPartition(keys[lo..(hi+1)], values[lo..(hi+1)]);
+ int p = PickPivotAndPartition(keys.Slice(0, partitionSize), values.Slice(0, partitionSize));
// Note we've already partitioned around the pivot and do not have to move the pivot again.
- IntroSort(keys[(p+1)..(hi+1)], values[(p+1)..(hi+1)], depthLimit);
+ IntroSort(keys[(p+1)..partitionSize], values[(p+1)..partitionSize], depthLimit);
hi = p - 1;
}
}
private static int PickPivotAndPartition(Span keys, Span values)
{
- Debug.Assert(!keys.IsEmpty);
+ Debug.Assert(keys.Length >= Array.IntrosortSizeThreshold);
- int lo = 0;
int hi = keys.Length - 1;
// Compute median-of-three. But also partition them, since we've done the comparison.
- int middle = lo + ((hi - lo) / 2);
+ int middle = hi >> 1;
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
- SwapIfGreaterWithValues(keys, values, lo, middle); // swap the low with the mid point
- SwapIfGreaterWithValues(keys, values, lo, hi); // swap the low with the high
+ SwapIfGreaterWithValues(keys, values, 0, middle); // swap the low with the mid point
+ SwapIfGreaterWithValues(keys, values, 0, hi); // swap the low with the high
SwapIfGreaterWithValues(keys, values, middle, hi); // swap the middle with the high
TKey pivot = keys[middle];
Swap(keys, values, middle, hi - 1);
- int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
+ int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
while (left < right)
{
if (pivot == null)
{
while (left < (hi - 1) && keys[++left] == null) ;
- while (right > lo && keys[--right] != null) ;
+ while (right > 0 && keys[--right] != null) ;
}
else
{
@@ -979,7 +936,10 @@ namespace System.Collections.Generic
}
// Put pivot in the right location.
- Swap(keys, values, left, hi - 1);
+ if (left != hi - 1)
+ {
+ Swap(keys, values, left, hi - 1);
+ }
return left;
}
@@ -987,19 +947,16 @@ namespace System.Collections.Generic
{
Debug.Assert(!keys.IsEmpty);
- int lo = 0;
- int hi = keys.Length - 1;
-
- int n = hi - lo + 1;
- for (int i = n / 2; i >= 1; i--)
+ int n = keys.Length;
+ for (int i = n >> 1; i >= 1; i--)
{
- DownHeap(keys, values, i, n, lo);
+ DownHeap(keys, values, i, n, 0);
}
for (int i = n; i > 1; i--)
{
- Swap(keys, values, lo, lo + i - 1);
- DownHeap(keys, values, 1, i - 1, lo);
+ Swap(keys, values, 0, i - 1);
+ DownHeap(keys, values, 1, i - 1, 0);
}
}
@@ -1011,7 +968,7 @@ namespace System.Collections.Generic
TKey d = keys[lo + i - 1];
TValue dValue = values[lo + i - 1];
- while (i <= n / 2)
+ while (i <= n >> 1)
{
int child = 2 * i;
if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs
index 76a6333ce..84ea4bf24 100644
--- a/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs
@@ -512,7 +512,6 @@ namespace System.Collections.Generic
if (behavior == InsertionBehavior.OverwriteExisting)
{
entries[i].value = value;
- _version++;
return true;
}
@@ -555,7 +554,6 @@ namespace System.Collections.Generic
if (behavior == InsertionBehavior.OverwriteExisting)
{
entries[i].value = value;
- _version++;
return true;
}
@@ -595,7 +593,6 @@ namespace System.Collections.Generic
if (behavior == InsertionBehavior.OverwriteExisting)
{
entries[i].value = value;
- _version++;
return true;
}
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlySet.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlySet.cs
new file mode 100644
index 000000000..8a2bf719c
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlySet.cs
@@ -0,0 +1,62 @@
+// 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.
+
+namespace System.Collections.Generic
+{
+ ///
+ /// Provides a readonly abstraction of a set.
+ ///
+ /// The type of elements in the set.
+ public interface IReadOnlySet : IReadOnlyCollection
+ {
+ ///
+ /// Determines if the set contains a specific item
+ ///
+ /// The item to check if the set contains.
+ /// if found; otherwise .
+ bool Contains(T item);
+ ///
+ /// Determines whether the current set is a proper (strict) subset of a specified collection.
+ ///
+ /// The collection to compare to the current set.
+ /// if the current set is a proper subset of other; otherwise .
+ /// other is .
+ bool IsProperSubsetOf(IEnumerable other);
+ ///
+ /// Determines whether the current set is a proper (strict) superset of a specified collection.
+ ///
+ /// The collection to compare to the current set.
+ /// if the collection is a proper superset of other; otherwise .
+ /// other is .
+ bool IsProperSupersetOf(IEnumerable other);
+ ///
+ /// Determine whether the current set is a subset of a specified collection.
+ ///
+ /// The collection to compare to the current set.
+ /// if the current set is a subset of other; otherwise .
+ /// other is .
+ bool IsSubsetOf(IEnumerable other);
+ ///
+ /// Determine whether the current set is a super set of a specified collection.
+ ///
+ /// The collection to compare to the current set
+ /// if the current set is a subset of other; otherwise .
+ /// other is .
+ bool IsSupersetOf(IEnumerable other);
+ ///
+ /// Determines whether the current set overlaps with the specified collection.
+ ///
+ /// The collection to compare to the current set.
+ /// if the current set and other share at least one common element; otherwise, .
+ /// other is .
+ bool Overlaps(IEnumerable other);
+ ///
+ /// Determines whether the current set and the specified collection contain the same elements.
+ ///
+ /// The collection to compare to the current set.
+ /// if the current set is equal to other; otherwise, .
+ /// other is .
+ bool SetEquals(IEnumerable other);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/ISet.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/ISet.cs
index 657adf787..ce71acba6 100644
--- a/src/System.Private.CoreLib/shared/System/Collections/Generic/ISet.cs
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/ISet.cs
@@ -2,8 +2,6 @@
// 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;
-
namespace System.Collections.Generic
{
///
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Hashtable.cs b/src/System.Private.CoreLib/shared/System/Collections/Hashtable.cs
index e90c277cc..705fe24db 100644
--- a/src/System.Private.CoreLib/shared/System/Collections/Hashtable.cs
+++ b/src/System.Private.CoreLib/shared/System/Collections/Hashtable.cs
@@ -480,11 +480,9 @@ namespace System.Collections
throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key);
}
- uint seed;
- uint incr;
// Take a snapshot of buckets, in case another thread resizes table
bucket[] lbuckets = _buckets;
- uint hashcode = InitHash(key, lbuckets.Length, out seed, out incr);
+ uint hashcode = InitHash(key, lbuckets.Length, out uint seed, out uint incr);
int ntry = 0;
bucket b;
@@ -639,12 +637,10 @@ namespace System.Collections
throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key);
}
- uint seed;
- uint incr;
// Take a snapshot of buckets, in case another thread does a resize
bucket[] lbuckets = _buckets;
- uint hashcode = InitHash(key, lbuckets.Length, out seed, out incr);
+ uint hashcode = InitHash(key, lbuckets.Length, out uint seed, out uint incr);
int ntry = 0;
bucket b;
@@ -858,11 +854,9 @@ namespace System.Collections
rehash();
}
- uint seed;
- uint incr;
// Assume we only have one thread writing concurrently. Modify
// buckets to contain new data, as long as we insert in the right order.
- uint hashcode = InitHash(key, _buckets.Length, out seed, out incr);
+ uint hashcode = InitHash(key, _buckets.Length, out uint seed, out uint incr);
int ntry = 0;
int emptySlotNumber = -1; // We use the empty slot number to cache the first empty slot. We chose to reuse slots
// create by remove that have the collision bit set over using up new slots.
@@ -994,10 +988,8 @@ namespace System.Collections
Debug.Assert(!_isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized.");
- uint seed;
- uint incr;
// Assuming only one concurrent writer, write directly into buckets.
- uint hashcode = InitHash(key, _buckets.Length, out seed, out incr);
+ uint hashcode = InitHash(key, _buckets.Length, out uint seed, out uint incr);
int ntry = 0;
bucket b;
@@ -1116,8 +1108,7 @@ namespace System.Collections
return;
}
- SerializationInfo? siInfo;
- HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
+ HashHelpers.SerializationInfoTable.TryGetValue(this, out SerializationInfo? siInfo);
if (siInfo == null)
{
diff --git a/src/System.Private.CoreLib/shared/System/Convert.cs b/src/System.Private.CoreLib/shared/System/Convert.cs
index 50ee261c3..ec6b92a96 100644
--- a/src/System.Private.CoreLib/shared/System/Convert.cs
+++ b/src/System.Private.CoreLib/shared/System/Convert.cs
@@ -528,11 +528,7 @@ namespace System
return (char)value;
}
- public static char ToChar(int value)
- {
- if (value < 0 || value > char.MaxValue) ThrowCharOverflowException();
- return (char)value;
- }
+ public static char ToChar(int value) => ToChar((uint)value);
[CLSCompliant(false)]
public static char ToChar(uint value)
@@ -541,11 +537,7 @@ namespace System
return (char)value;
}
- public static char ToChar(long value)
- {
- if (value < 0 || value > char.MaxValue) ThrowCharOverflowException();
- return (char)value;
- }
+ public static char ToChar(long value) => ToChar((ulong)value);
[CLSCompliant(false)]
public static char ToChar(ulong value)
@@ -667,7 +659,7 @@ namespace System
[CLSCompliant(false)]
public static sbyte ToSByte(uint value)
{
- if (value > sbyte.MaxValue) ThrowSByteOverflowException();
+ if (value > (uint)sbyte.MaxValue) ThrowSByteOverflowException();
return (sbyte)value;
}
@@ -708,13 +700,13 @@ namespace System
{
if (value == null)
return 0;
- return sbyte.Parse(value, CultureInfo.CurrentCulture);
+ return sbyte.Parse(value);
}
[CLSCompliant(false)]
public static sbyte ToSByte(string value, IFormatProvider? provider)
{
- return sbyte.Parse(value, NumberStyles.Integer, provider);
+ return sbyte.Parse(value, provider);
}
[CLSCompliant(false)]
@@ -757,13 +749,13 @@ namespace System
[CLSCompliant(false)]
public static byte ToByte(sbyte value)
{
- if (value < byte.MinValue) ThrowByteOverflowException();
+ if (value < 0) ThrowByteOverflowException();
return (byte)value;
}
public static byte ToByte(short value)
{
- if (value < byte.MinValue || value > byte.MaxValue) ThrowByteOverflowException();
+ if ((uint)value > byte.MaxValue) ThrowByteOverflowException();
return (byte)value;
}
@@ -774,11 +766,7 @@ namespace System
return (byte)value;
}
- public static byte ToByte(int value)
- {
- if (value < byte.MinValue || value > byte.MaxValue) ThrowByteOverflowException();
- return (byte)value;
- }
+ public static byte ToByte(int value) => ToByte((uint)value);
[CLSCompliant(false)]
public static byte ToByte(uint value)
@@ -787,11 +775,7 @@ namespace System
return (byte)value;
}
- public static byte ToByte(long value)
- {
- if (value < byte.MinValue || value > byte.MaxValue) ThrowByteOverflowException();
- return (byte)value;
- }
+ public static byte ToByte(long value) => ToByte((ulong)value);
[CLSCompliant(false)]
public static byte ToByte(ulong value)
@@ -819,14 +803,14 @@ namespace System
{
if (value == null)
return 0;
- return byte.Parse(value, CultureInfo.CurrentCulture);
+ return byte.Parse(value);
}
public static byte ToByte(string? value, IFormatProvider? provider)
{
if (value == null)
return 0;
- return byte.Parse(value, NumberStyles.Integer, provider);
+ return byte.Parse(value, provider);
}
public static byte ToByte(DateTime value)
@@ -887,7 +871,7 @@ namespace System
[CLSCompliant(false)]
public static short ToInt16(uint value)
{
- if (value > short.MaxValue) ThrowInt16OverflowException();
+ if (value > (uint)short.MaxValue) ThrowInt16OverflowException();
return (short)value;
}
@@ -928,14 +912,14 @@ namespace System
{
if (value == null)
return 0;
- return short.Parse(value, CultureInfo.CurrentCulture);
+ return short.Parse(value);
}
public static short ToInt16(string? value, IFormatProvider? provider)
{
if (value == null)
return 0;
- return short.Parse(value, NumberStyles.Integer, provider);
+ return short.Parse(value, provider);
}
public static short ToInt16(DateTime value)
@@ -993,11 +977,7 @@ namespace System
}
[CLSCompliant(false)]
- public static ushort ToUInt16(int value)
- {
- if (value < 0 || value > ushort.MaxValue) ThrowUInt16OverflowException();
- return (ushort)value;
- }
+ public static ushort ToUInt16(int value) => ToUInt16((uint)value);
[CLSCompliant(false)]
public static ushort ToUInt16(ushort value)
@@ -1013,11 +993,7 @@ namespace System
}
[CLSCompliant(false)]
- public static ushort ToUInt16(long value)
- {
- if (value < 0 || value > ushort.MaxValue) ThrowUInt16OverflowException();
- return (ushort)value;
- }
+ public static ushort ToUInt16(long value) => ToUInt16((ulong)value);
[CLSCompliant(false)]
public static ushort ToUInt16(ulong value)
@@ -1049,7 +1025,7 @@ namespace System
{
if (value == null)
return 0;
- return ushort.Parse(value, CultureInfo.CurrentCulture);
+ return ushort.Parse(value);
}
[CLSCompliant(false)]
@@ -1057,7 +1033,7 @@ namespace System
{
if (value == null)
return 0;
- return ushort.Parse(value, NumberStyles.Integer, provider);
+ return ushort.Parse(value, provider);
}
[CLSCompliant(false)]
@@ -1116,7 +1092,7 @@ namespace System
[CLSCompliant(false)]
public static int ToInt32(uint value)
{
- if (value > int.MaxValue) ThrowInt32OverflowException();
+ if ((int)value < 0) ThrowInt32OverflowException();
return (int)value;
}
@@ -1177,14 +1153,14 @@ namespace System
{
if (value == null)
return 0;
- return int.Parse(value, CultureInfo.CurrentCulture);
+ return int.Parse(value);
}
public static int ToInt32(string? value, IFormatProvider? provider)
{
if (value == null)
return 0;
- return int.Parse(value, NumberStyles.Integer, provider);
+ return int.Parse(value, provider);
}
public static int ToInt32(DateTime value)
@@ -1261,11 +1237,7 @@ namespace System
}
[CLSCompliant(false)]
- public static uint ToUInt32(long value)
- {
- if (value < 0 || value > uint.MaxValue) ThrowUInt32OverflowException();
- return (uint)value;
- }
+ public static uint ToUInt32(long value) => ToUInt32((ulong)value);
[CLSCompliant(false)]
public static uint ToUInt32(ulong value)
@@ -1304,7 +1276,7 @@ namespace System
{
if (value == null)
return 0;
- return uint.Parse(value, CultureInfo.CurrentCulture);
+ return uint.Parse(value);
}
[CLSCompliant(false)]
@@ -1312,7 +1284,7 @@ namespace System
{
if (value == null)
return 0;
- return uint.Parse(value, NumberStyles.Integer, provider);
+ return uint.Parse(value, provider);
}
[CLSCompliant(false)]
@@ -1382,7 +1354,7 @@ namespace System
[CLSCompliant(false)]
public static long ToInt64(ulong value)
{
- if (value > long.MaxValue) ThrowInt64OverflowException();
+ if ((long)value < 0) ThrowInt64OverflowException();
return (long)value;
}
@@ -1410,14 +1382,14 @@ namespace System
{
if (value == null)
return 0;
- return long.Parse(value, CultureInfo.CurrentCulture);
+ return long.Parse(value);
}
public static long ToInt64(string? value, IFormatProvider? provider)
{
if (value == null)
return 0;
- return long.Parse(value, NumberStyles.Integer, provider);
+ return long.Parse(value, provider);
}
public static long ToInt64(DateTime value)
@@ -1529,7 +1501,7 @@ namespace System
{
if (value == null)
return 0;
- return ulong.Parse(value, CultureInfo.CurrentCulture);
+ return ulong.Parse(value);
}
[CLSCompliant(false)]
@@ -1537,7 +1509,7 @@ namespace System
{
if (value == null)
return 0;
- return ulong.Parse(value, NumberStyles.Integer, provider);
+ return ulong.Parse(value, provider);
}
[CLSCompliant(false)]
@@ -1629,14 +1601,14 @@ namespace System
{
if (value == null)
return 0;
- return float.Parse(value, CultureInfo.CurrentCulture);
+ return float.Parse(value);
}
public static float ToSingle(string? value, IFormatProvider? provider)
{
if (value == null)
return 0;
- return float.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, provider);
+ return float.Parse(value, provider);
}
public static float ToSingle(bool value)
@@ -1732,14 +1704,14 @@ namespace System
{
if (value == null)
return 0;
- return double.Parse(value, CultureInfo.CurrentCulture);
+ return double.Parse(value);
}
public static double ToDouble(string? value, IFormatProvider? provider)
{
if (value == null)
return 0;
- return double.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, provider);
+ return double.Parse(value, provider);
}
public static double ToDouble(bool value)
@@ -1830,14 +1802,14 @@ namespace System
{
if (value == null)
return 0m;
- return decimal.Parse(value, CultureInfo.CurrentCulture);
+ return decimal.Parse(value);
}
public static decimal ToDecimal(string? value, IFormatProvider? provider)
{
if (value == null)
return 0m;
- return decimal.Parse(value, NumberStyles.Number, provider);
+ return decimal.Parse(value, provider);
}
public static decimal ToDecimal(decimal value)
@@ -1879,7 +1851,7 @@ namespace System
{
if (value == null)
return new DateTime(0);
- return DateTime.Parse(value, CultureInfo.CurrentCulture);
+ return DateTime.Parse(value);
}
public static DateTime ToDateTime(string? value, IFormatProvider? provider)
@@ -2000,7 +1972,7 @@ namespace System
[CLSCompliant(false)]
public static string ToString(sbyte value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
[CLSCompliant(false)]
@@ -2011,7 +1983,7 @@ namespace System
public static string ToString(byte value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
public static string ToString(byte value, IFormatProvider? provider)
@@ -2021,7 +1993,7 @@ namespace System
public static string ToString(short value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
public static string ToString(short value, IFormatProvider? provider)
@@ -2032,7 +2004,7 @@ namespace System
[CLSCompliant(false)]
public static string ToString(ushort value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
[CLSCompliant(false)]
@@ -2043,7 +2015,7 @@ namespace System
public static string ToString(int value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
public static string ToString(int value, IFormatProvider? provider)
@@ -2054,7 +2026,7 @@ namespace System
[CLSCompliant(false)]
public static string ToString(uint value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
[CLSCompliant(false)]
@@ -2065,7 +2037,7 @@ namespace System
public static string ToString(long value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
public static string ToString(long value, IFormatProvider? provider)
@@ -2076,7 +2048,7 @@ namespace System
[CLSCompliant(false)]
public static string ToString(ulong value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
[CLSCompliant(false)]
@@ -2087,7 +2059,7 @@ namespace System
public static string ToString(float value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
public static string ToString(float value, IFormatProvider? provider)
@@ -2097,7 +2069,7 @@ namespace System
public static string ToString(double value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
public static string ToString(double value, IFormatProvider? provider)
@@ -2107,7 +2079,7 @@ namespace System
public static string ToString(decimal value)
{
- return value.ToString(CultureInfo.CurrentCulture);
+ return value.ToString();
}
public static string ToString(decimal value, IFormatProvider? provider)
@@ -2157,7 +2129,7 @@ namespace System
}
int r = ParseNumbers.StringToInt(value.AsSpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned);
- if (r < byte.MinValue || r > byte.MaxValue)
+ if ((uint)r > byte.MaxValue)
ThrowByteOverflowException();
return (byte)r;
}
@@ -2231,7 +2203,7 @@ namespace System
}
int r = ParseNumbers.StringToInt(value.AsSpan(), fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned);
- if (r < ushort.MinValue || r > ushort.MaxValue)
+ if ((uint)r > ushort.MaxValue)
ThrowUInt16OverflowException();
return (ushort)r;
}
diff --git a/src/System.Private.CoreLib/shared/System/DateTime.cs b/src/System.Private.CoreLib/shared/System/DateTime.cs
index a03da9563..023d79501 100644
--- a/src/System.Private.CoreLib/shared/System/DateTime.cs
+++ b/src/System.Private.CoreLib/shared/System/DateTime.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
@@ -751,8 +752,7 @@ namespace System
// Because the ticks conversion between UTC and local is lossy, we need to capture whether the
// time is in a repeated hour so that it can be passed to the DateTime constructor.
DateTime utcDt = new DateTime(ticks, DateTimeKind.Utc);
- bool isDaylightSavings = false;
- offsetTicks = TimeZoneInfo.GetUtcOffsetFromUtc(utcDt, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks;
+ offsetTicks = TimeZoneInfo.GetUtcOffsetFromUtc(utcDt, TimeZoneInfo.Local, out bool isDaylightSavings, out isAmbiguousLocalDst).Ticks;
}
ticks += offsetTicks;
// Another behaviour of parsing is to cause small times to wrap around, so that they can be used
@@ -1079,8 +1079,7 @@ namespace System
get
{
DateTime utc = UtcNow;
- bool isAmbiguousLocalDst;
- long offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out isAmbiguousLocalDst).Ticks;
+ long offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out bool isAmbiguousLocalDst).Ticks;
long tick = utc.Ticks + offset;
if (tick > DateTime.MaxTicks)
{
@@ -1290,9 +1289,7 @@ namespace System
{
return this;
}
- bool isDaylightSavings = false;
- bool isAmbiguousLocalDst = false;
- long offset = TimeZoneInfo.GetUtcOffsetFromUtc(this, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks;
+ long offset = TimeZoneInfo.GetUtcOffsetFromUtc(this, TimeZoneInfo.Local, out bool isDaylightSavings, out bool isAmbiguousLocalDst).Ticks;
long tick = Ticks + offset;
if (tick > DateTime.MaxTicks)
{
@@ -1359,7 +1356,7 @@ namespace System
return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
}
- public static bool TryParse(string? s, out DateTime result)
+ public static bool TryParse([NotNullWhen(true)] string? s, out DateTime result)
{
if (s == null)
{
@@ -1374,7 +1371,7 @@ namespace System
return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result);
}
- public static bool TryParse(string? s, IFormatProvider? provider, DateTimeStyles styles, out DateTime result)
+ public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, DateTimeStyles styles, out DateTime result)
{
DateTimeFormatInfo.ValidateStyles(styles, nameof(styles));
@@ -1393,7 +1390,7 @@ namespace System
return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result);
}
- public static bool TryParseExact(string? s, string? format, IFormatProvider? provider, DateTimeStyles style, out DateTime result)
+ public static bool TryParseExact([NotNullWhen(true)] string? s, [NotNullWhen(true)] string? format, IFormatProvider? provider, DateTimeStyles style, out DateTime result)
{
DateTimeFormatInfo.ValidateStyles(style, nameof(style));
@@ -1412,7 +1409,7 @@ namespace System
return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result);
}
- public static bool TryParseExact(string? s, string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateTime result)
+ public static bool TryParseExact([NotNullWhen(true)] string? s, [NotNullWhen(true)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateTime result)
{
DateTimeFormatInfo.ValidateStyles(style, nameof(style));
@@ -1425,7 +1422,7 @@ namespace System
return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result);
}
- public static bool TryParseExact(ReadOnlySpan s, string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateTime result)
+ public static bool TryParseExact(ReadOnlySpan s, [NotNullWhen(true)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateTime result)
{
DateTimeFormatInfo.ValidateStyles(style, nameof(style));
return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result);
diff --git a/src/System.Private.CoreLib/shared/System/DateTimeOffset.cs b/src/System.Private.CoreLib/shared/System/DateTimeOffset.cs
index 81a061241..45338f361 100644
--- a/src/System.Private.CoreLib/shared/System/DateTimeOffset.cs
+++ b/src/System.Private.CoreLib/shared/System/DateTimeOffset.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
@@ -479,11 +480,10 @@ namespace System
{
if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
- TimeSpan offset;
DateTime dateResult = DateTimeParse.Parse(input,
DateTimeFormatInfo.CurrentInfo,
DateTimeStyles.None,
- out offset);
+ out TimeSpan offset);
return new DateTimeOffset(dateResult.Ticks, offset);
}
@@ -502,11 +502,10 @@ namespace System
styles = ValidateStyles(styles, nameof(styles));
if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
- TimeSpan offset;
DateTime dateResult = DateTimeParse.Parse(input,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
- out offset);
+ out TimeSpan offset);
return new DateTimeOffset(dateResult.Ticks, offset);
}
@@ -538,12 +537,11 @@ namespace System
if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format);
- TimeSpan offset;
DateTime dateResult = DateTimeParse.ParseExact(input,
format,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
- out offset);
+ out TimeSpan offset);
return new DateTimeOffset(dateResult.Ticks, offset);
}
@@ -559,12 +557,11 @@ namespace System
styles = ValidateStyles(styles, nameof(styles));
if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
- TimeSpan offset;
DateTime dateResult = DateTimeParse.ParseExactMultiple(input,
formats,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
- out offset);
+ out TimeSpan offset);
return new DateTimeOffset(dateResult.Ticks, offset);
}
@@ -652,15 +649,13 @@ namespace System
public DateTimeOffset ToUniversalTime() =>
new DateTimeOffset(UtcDateTime);
- public static bool TryParse(string? input, out DateTimeOffset result)
+ public static bool TryParse([NotNullWhen(true)] string? input, out DateTimeOffset result)
{
- TimeSpan offset;
- DateTime dateResult;
bool parsed = DateTimeParse.TryParse(input,
DateTimeFormatInfo.CurrentInfo,
DateTimeStyles.None,
- out dateResult,
- out offset);
+ out DateTime dateResult,
+ out TimeSpan offset);
result = new DateTimeOffset(dateResult.Ticks, offset);
return parsed;
}
@@ -672,7 +667,7 @@ namespace System
return parsed;
}
- public static bool TryParse(string? input, IFormatProvider? formatProvider, DateTimeStyles styles, out DateTimeOffset result)
+ public static bool TryParse([NotNullWhen(true)] string? input, IFormatProvider? formatProvider, DateTimeStyles styles, out DateTimeOffset result)
{
styles = ValidateStyles(styles, nameof(styles));
if (input == null)
@@ -681,13 +676,11 @@ namespace System
return false;
}
- TimeSpan offset;
- DateTime dateResult;
bool parsed = DateTimeParse.TryParse(input,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
- out dateResult,
- out offset);
+ out DateTime dateResult,
+ out TimeSpan offset);
result = new DateTimeOffset(dateResult.Ticks, offset);
return parsed;
}
@@ -700,7 +693,7 @@ namespace System
return parsed;
}
- public static bool TryParseExact(string? input, string? format, IFormatProvider? formatProvider, DateTimeStyles styles,
+ public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true)] string? format, IFormatProvider? formatProvider, DateTimeStyles styles,
out DateTimeOffset result)
{
styles = ValidateStyles(styles, nameof(styles));
@@ -710,14 +703,12 @@ namespace System
return false;
}
- TimeSpan offset;
- DateTime dateResult;
bool parsed = DateTimeParse.TryParseExact(input,
format,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
- out dateResult,
- out offset);
+ out DateTime dateResult,
+ out TimeSpan offset);
result = new DateTimeOffset(dateResult.Ticks, offset);
return parsed;
}
@@ -731,7 +722,7 @@ namespace System
return parsed;
}
- public static bool TryParseExact(string? input, string?[]? formats, IFormatProvider? formatProvider, DateTimeStyles styles,
+ public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true)] string?[]? formats, IFormatProvider? formatProvider, DateTimeStyles styles,
out DateTimeOffset result)
{
styles = ValidateStyles(styles, nameof(styles));
@@ -741,20 +732,18 @@ namespace System
return false;
}
- TimeSpan offset;
- DateTime dateResult;
bool parsed = DateTimeParse.TryParseExactMultiple(input,
formats,
DateTimeFormatInfo.GetInstance(formatProvider),
styles,
- out dateResult,
- out offset);
+ out DateTime dateResult,
+ out TimeSpan offset);
result = new DateTimeOffset(dateResult.Ticks, offset);
return parsed;
}
public static bool TryParseExact(
- ReadOnlySpan input, string?[]? formats, IFormatProvider? formatProvider, DateTimeStyles styles, out DateTimeOffset result)
+ ReadOnlySpan input, [NotNullWhen(true)] string?[]? formats, IFormatProvider? formatProvider, DateTimeStyles styles, out DateTimeOffset result)
{
styles = ValidateStyles(styles, nameof(styles));
bool parsed = DateTimeParse.TryParseExactMultiple(input, formats, DateTimeFormatInfo.GetInstance(formatProvider), styles, out DateTime dateResult, out TimeSpan offset);
diff --git a/src/System.Private.CoreLib/shared/System/Decimal.cs b/src/System.Private.CoreLib/shared/System/Decimal.cs
index da61628ab..cc32ce562 100644
--- a/src/System.Private.CoreLib/shared/System/Decimal.cs
+++ b/src/System.Private.CoreLib/shared/System/Decimal.cs
@@ -4,6 +4,7 @@
using System.Buffers.Binary;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -480,7 +481,7 @@ namespace System
return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider));
}
- public static bool TryParse(string? s, out decimal result)
+ public static bool TryParse([NotNullWhen(true)] string? s, out decimal result)
{
if (s == null)
{
@@ -496,7 +497,7 @@ namespace System
return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
}
- public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out decimal result)
+ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out decimal result)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
@@ -936,9 +937,9 @@ namespace System
[CLSCompliant(false)]
public static explicit operator ulong(decimal value) => ToUInt64(value);
- public static explicit operator float(decimal value) => ToSingle(value);
+ public static explicit operator float(decimal value) => DecCalc.VarR4FromDec(in value);
- public static explicit operator double(decimal value) => ToDouble(value);
+ public static explicit operator double(decimal value) => DecCalc.VarR8FromDec(in value);
public static decimal operator +(decimal d) => d;
@@ -1051,12 +1052,12 @@ namespace System
float IConvertible.ToSingle(IFormatProvider? provider)
{
- return Convert.ToSingle(this);
+ return DecCalc.VarR4FromDec(in this);
}
double IConvertible.ToDouble(IFormatProvider? provider)
{
- return Convert.ToDouble(this);
+ return DecCalc.VarR8FromDec(in this);
}
decimal IConvertible.ToDecimal(IFormatProvider? provider)
diff --git a/src/System.Private.CoreLib/shared/System/DefaultBinder.cs b/src/System.Private.CoreLib/shared/System/DefaultBinder.cs
index fed9a020f..2f76cbc8a 100644
--- a/src/System.Private.CoreLib/shared/System/DefaultBinder.cs
+++ b/src/System.Private.CoreLib/shared/System/DefaultBinder.cs
@@ -600,7 +600,6 @@ namespace System
{
if (newMin == 2)
{
- currentMin = i;
ambig = false;
currentMin = i;
}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/CodeAnalysis/NullableAttributes.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
index 0b24c01a7..2c4e91c3e 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
@@ -125,4 +125,71 @@ namespace System.Diagnostics.CodeAnalysis
/// Gets the condition parameter value.
public bool ParameterValue { get; }
}
+
+ /// Specifies that the method or property will ensure that the listed field and property members have not-null values.
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class MemberNotNullAttribute : Attribute
+ {
+ /// Initializes the attribute with a field or property member.
+ ///
+ /// The field or property member that is promised to be not-null.
+ ///
+ public MemberNotNullAttribute(string member) => Members = new[] { member };
+
+ /// Initializes the attribute with the list of field and property members.
+ ///
+ /// The list of field and property members that are promised to be not-null.
+ ///
+ public MemberNotNullAttribute(params string[] members) => Members = members;
+
+ /// Gets field or property member names.
+ public string[] Members { get; }
+ }
+
+ /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition.
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class MemberNotNullWhenAttribute : Attribute
+ {
+ /// Initializes the attribute with the specified return value condition and a field or property member.
+ ///
+ /// The return value condition. If the method returns this value, the associated parameter will not be null.
+ ///
+ ///
+ /// The field or property member that is promised to be not-null.
+ ///
+ public MemberNotNullWhenAttribute(bool returnValue, string member)
+ {
+ ReturnValue = returnValue;
+ Members = new[] { member };
+ }
+
+ /// Initializes the attribute with the specified return value condition and list of field and property members.
+ ///
+ /// The return value condition. If the method returns this value, the associated parameter will not be null.
+ ///
+ ///
+ /// The list of field and property members that are promised to be not-null.
+ ///
+ public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
+ {
+ ReturnValue = returnValue;
+ Members = members;
+ }
+
+ /// Gets the return value condition.
+ public bool ReturnValue { get; }
+
+ /// Gets field or property member names.
+ public string[] Members { get; }
+ }
}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/DebugProvider.Unix.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/DebugProvider.Unix.cs
index 9259c4fbe..2cc40f94e 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/DebugProvider.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/DebugProvider.Unix.cs
@@ -2,8 +2,6 @@
// 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.Win32.SafeHandles;
-
namespace System.Diagnostics
{
public partial class DebugProvider
@@ -56,7 +54,7 @@ namespace System.Diagnostics
}
else
{
- Interop.Sys.SysLog(Interop.Sys.SysLogPriority.LOG_USER | Interop.Sys.SysLogPriority.LOG_DEBUG, "%s", message);
+ Interop.Sys.SysLog(Interop.Sys.SysLogPriority.LOG_DEBUG, "%s", message);
}
}
@@ -89,7 +87,7 @@ namespace System.Diagnostics
int totalBytesWritten = 0;
while (bufCount > 0)
{
- int bytesWritten = Interop.Sys.Write(2 /* stderr */, buf + totalBytesWritten, bufCount);
+ int bytesWritten = Interop.Sys.Write((IntPtr)2 /* stderr */, buf + totalBytesWritten, bufCount);
if (bytesWritten < 0)
{
// On error, simply stop writing the debug output. This could commonly happen if stderr
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs
index e4086d1d6..987a554f4 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs
@@ -102,8 +102,7 @@ namespace System.Diagnostics.Tracing
EnsureEventSourceIndexAvailable(eventSourceIndex);
Debug.Assert(s_counterGroups != null);
WeakReference weakRef = CounterGroup.s_counterGroups[eventSourceIndex];
- CounterGroup? ret = null;
- if (weakRef == null || !weakRef.TryGetTarget(out ret))
+ if (weakRef == null || !weakRef.TryGetTarget(out CounterGroup? ret))
{
ret = new CounterGroup(eventSource);
CounterGroup.s_counterGroups[eventSourceIndex] = new WeakReference(ret);
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipe.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipe.cs
new file mode 100644
index 000000000..308c19ada
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipe.cs
@@ -0,0 +1,277 @@
+// 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.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+#if FEATURE_PERFTRACING
+
+namespace System.Diagnostics.Tracing
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct EventPipeEventInstanceData
+ {
+ internal IntPtr ProviderID;
+ internal uint EventID;
+ internal uint ThreadID;
+ internal long TimeStamp;
+ internal Guid ActivityId;
+ internal Guid ChildActivityId;
+ internal IntPtr Payload;
+ internal uint PayloadLength;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct EventPipeSessionInfo
+ {
+ internal long StartTimeAsUTCFileTime;
+ internal long StartTimeStamp;
+ internal long TimeStampFrequency;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct EventPipeProviderConfiguration
+ {
+ [MarshalAs(UnmanagedType.LPWStr)]
+ private readonly string m_providerName;
+ private readonly ulong m_keywords;
+ private readonly uint m_loggingLevel;
+
+ [MarshalAs(UnmanagedType.LPWStr)]
+ private readonly string? m_filterData;
+
+ internal EventPipeProviderConfiguration(
+ string providerName,
+ ulong keywords,
+ uint loggingLevel,
+ string? filterData)
+ {
+ if (string.IsNullOrEmpty(providerName))
+ {
+ throw new ArgumentNullException(nameof(providerName));
+ }
+ if (loggingLevel > 5) // 5 == Verbose, the highest value in EventPipeLoggingLevel.
+ {
+ throw new ArgumentOutOfRangeException(nameof(loggingLevel));
+ }
+ m_providerName = providerName;
+ m_keywords = keywords;
+ m_loggingLevel = loggingLevel;
+ m_filterData = filterData;
+ }
+
+ internal string ProviderName
+ {
+ get { return m_providerName; }
+ }
+
+ internal ulong Keywords
+ {
+ get { return m_keywords; }
+ }
+
+ internal uint LoggingLevel
+ {
+ get { return m_loggingLevel; }
+ }
+
+ internal string? FilterData => m_filterData;
+ }
+
+ internal enum EventPipeSerializationFormat
+ {
+ NetPerf,
+ NetTrace
+ }
+
+ internal sealed class EventPipeWaitHandle : WaitHandle
+ {
+ }
+
+ internal sealed class EventPipeConfiguration
+ {
+ private readonly string m_outputFile;
+ private readonly EventPipeSerializationFormat m_format;
+ private readonly uint m_circularBufferSizeInMB;
+ private readonly List m_providers;
+ private TimeSpan m_minTimeBetweenSamples = TimeSpan.FromMilliseconds(1);
+
+ internal EventPipeConfiguration(
+ string outputFile,
+ EventPipeSerializationFormat format,
+ uint circularBufferSizeInMB)
+ {
+ if (string.IsNullOrEmpty(outputFile))
+ {
+ throw new ArgumentNullException(nameof(outputFile));
+ }
+ if (circularBufferSizeInMB == 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(circularBufferSizeInMB));
+ }
+ m_outputFile = outputFile;
+ m_format = format;
+ m_circularBufferSizeInMB = circularBufferSizeInMB;
+ m_providers = new List();
+ }
+
+ internal string OutputFile
+ {
+ get { return m_outputFile; }
+ }
+
+ internal EventPipeSerializationFormat Format
+ {
+ get { return m_format; }
+ }
+
+ internal uint CircularBufferSizeInMB
+ {
+ get { return m_circularBufferSizeInMB; }
+ }
+
+ internal EventPipeProviderConfiguration[] Providers
+ {
+ get { return m_providers.ToArray(); }
+ }
+
+ internal void EnableProvider(string providerName, ulong keywords, uint loggingLevel)
+ {
+ EnableProviderWithFilter(providerName, keywords, loggingLevel, null);
+ }
+
+ internal void EnableProviderWithFilter(string providerName, ulong keywords, uint loggingLevel, string? filterData)
+ {
+ m_providers.Add(new EventPipeProviderConfiguration(
+ providerName,
+ keywords,
+ loggingLevel,
+ filterData));
+ }
+
+ private void EnableProviderConfiguration(EventPipeProviderConfiguration providerConfig)
+ {
+ m_providers.Add(providerConfig);
+ }
+
+ internal void EnableProviderRange(EventPipeProviderConfiguration[] providerConfigs)
+ {
+ foreach (EventPipeProviderConfiguration config in providerConfigs)
+ {
+ EnableProviderConfiguration(config);
+ }
+ }
+
+ internal void SetProfilerSamplingRate(TimeSpan minTimeBetweenSamples)
+ {
+ if (minTimeBetweenSamples.Ticks <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(minTimeBetweenSamples));
+ }
+
+ m_minTimeBetweenSamples = minTimeBetweenSamples;
+ }
+ }
+
+ internal static class EventPipe
+ {
+ private static ulong s_sessionID = 0;
+
+ internal static void Enable(EventPipeConfiguration configuration)
+ {
+ if (configuration == null)
+ {
+ throw new ArgumentNullException(nameof(configuration));
+ }
+
+ if (configuration.Providers == null)
+ {
+ throw new ArgumentNullException(nameof(configuration.Providers));
+ }
+
+ EventPipeProviderConfiguration[] providers = configuration.Providers;
+
+ s_sessionID = EventPipeInternal.Enable(
+ configuration.OutputFile,
+ configuration.Format,
+ configuration.CircularBufferSizeInMB,
+ providers);
+ }
+
+ internal static void Disable()
+ {
+ EventPipeInternal.Disable(s_sessionID);
+ }
+ }
+
+ internal static partial class EventPipeInternal
+ {
+ private unsafe struct EventPipeProviderConfigurationNative
+ {
+ private char* m_pProviderName;
+ private ulong m_keywords;
+ private uint m_loggingLevel;
+ private char* m_pFilterData;
+
+ internal static void MarshalToNative(EventPipeProviderConfiguration managed, ref EventPipeProviderConfigurationNative native)
+ {
+ native.m_pProviderName = (char*)Marshal.StringToCoTaskMemUni(managed.ProviderName);
+ native.m_keywords = managed.Keywords;
+ native.m_loggingLevel = managed.LoggingLevel;
+ native.m_pFilterData = (char*)Marshal.StringToCoTaskMemUni(managed.FilterData);
+ }
+
+ internal void Release()
+ {
+ if (m_pProviderName != null)
+ {
+ Marshal.FreeCoTaskMem((IntPtr)m_pProviderName);
+ }
+ if (m_pFilterData != null)
+ {
+ Marshal.FreeCoTaskMem((IntPtr)m_pFilterData);
+ }
+ }
+ }
+
+ internal static unsafe ulong Enable(
+ string? outputFile,
+ EventPipeSerializationFormat format,
+ uint circularBufferSizeInMB,
+ EventPipeProviderConfiguration[] providers)
+ {
+ Span providersNative = new Span((void*)Marshal.AllocCoTaskMem(sizeof(EventPipeProviderConfigurationNative) * providers.Length), providers.Length);
+ providersNative.Clear();
+
+ try
+ {
+ for (int i = 0; i < providers.Length; i++)
+ {
+ EventPipeProviderConfigurationNative.MarshalToNative(providers[i], ref providersNative[i]);
+ }
+
+ fixed (char* outputFilePath = outputFile)
+ fixed (EventPipeProviderConfigurationNative* providersNativePointer = providersNative)
+ {
+ return Enable(outputFilePath, format, circularBufferSizeInMB, providersNativePointer, (uint)providersNative.Length);
+ }
+ }
+ finally
+ {
+ for (int i = 0; i < providers.Length; i++)
+ {
+ providersNative[i].Release();
+ }
+
+ fixed (EventPipeProviderConfigurationNative* providersNativePointer = providersNative)
+ {
+ Marshal.FreeCoTaskMem((IntPtr)providersNativePointer);
+ }
+ }
+ }
+ }
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipeEventDispatcher.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipeEventDispatcher.cs
new file mode 100644
index 000000000..f2d9b30bf
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipeEventDispatcher.cs
@@ -0,0 +1,226 @@
+// 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.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Diagnostics.Tracing
+{
+#if FEATURE_PERFTRACING
+ internal sealed class EventPipeEventDispatcher
+ {
+ internal sealed class EventListenerSubscription
+ {
+ internal EventKeywords MatchAnyKeywords { get; private set; }
+ internal EventLevel Level { get; private set; }
+
+ internal EventListenerSubscription(EventKeywords matchAnyKeywords, EventLevel level)
+ {
+ MatchAnyKeywords = matchAnyKeywords;
+ Level = level;
+ }
+ }
+
+ internal static readonly EventPipeEventDispatcher Instance = new EventPipeEventDispatcher();
+
+ private readonly IntPtr m_RuntimeProviderID;
+
+ private ulong m_sessionID = 0;
+ private DateTime m_syncTimeUtc;
+ private long m_syncTimeQPC;
+ private long m_timeQPCFrequency;
+
+ private bool m_stopDispatchTask;
+ private readonly EventPipeWaitHandle m_dispatchTaskWaitHandle = new EventPipeWaitHandle();
+ private Task? m_dispatchTask = null;
+ private readonly object m_dispatchControlLock = new object();
+ private readonly Dictionary m_subscriptions = new Dictionary();
+
+ private const uint DefaultEventListenerCircularMBSize = 10;
+
+ private EventPipeEventDispatcher()
+ {
+ // Get the ID of the runtime provider so that it can be used as a filter when processing events.
+ m_RuntimeProviderID = EventPipeInternal.GetProvider(NativeRuntimeEventSource.EventSourceName);
+ m_dispatchTaskWaitHandle.SafeWaitHandle = new SafeWaitHandle(IntPtr.Zero, false);
+ }
+
+ internal void SendCommand(EventListener eventListener, EventCommand command, bool enable, EventLevel level, EventKeywords matchAnyKeywords)
+ {
+ if (command == EventCommand.Update && enable)
+ {
+ lock (m_dispatchControlLock)
+ {
+ // Add the new subscription. This will overwrite an existing subscription for the listener if one exists.
+ m_subscriptions[eventListener] = new EventListenerSubscription(matchAnyKeywords, level);
+
+ // Commit the configuration change.
+ CommitDispatchConfiguration();
+ }
+ }
+ else if (command == EventCommand.Update && !enable)
+ {
+ RemoveEventListener(eventListener);
+ }
+ }
+
+ internal void RemoveEventListener(EventListener listener)
+ {
+ lock (m_dispatchControlLock)
+ {
+ // Remove the event listener from the list of subscribers.
+ if (m_subscriptions.ContainsKey(listener))
+ {
+ m_subscriptions.Remove(listener);
+ }
+
+ // Commit the configuration change.
+ CommitDispatchConfiguration();
+ }
+ }
+
+ private void CommitDispatchConfiguration()
+ {
+ Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));
+
+ // Ensure that the dispatch task is stopped.
+ // This is a no-op if the task is already stopped.
+ StopDispatchTask();
+
+ // Stop tracing.
+ // This is a no-op if it's already disabled.
+ EventPipeInternal.Disable(m_sessionID);
+
+ // Check to see if tracing should be enabled.
+ if (m_subscriptions.Count <= 0)
+ {
+ return;
+ }
+
+ // Determine the keywords and level that should be used based on the set of enabled EventListeners.
+ EventKeywords aggregatedKeywords = EventKeywords.None;
+ EventLevel highestLevel = EventLevel.LogAlways;
+
+ foreach (EventListenerSubscription subscription in m_subscriptions.Values)
+ {
+ aggregatedKeywords |= subscription.MatchAnyKeywords;
+ highestLevel = (subscription.Level > highestLevel) ? subscription.Level : highestLevel;
+ }
+
+ // Enable the EventPipe session.
+ EventPipeProviderConfiguration[] providerConfiguration = new EventPipeProviderConfiguration[]
+ {
+ new EventPipeProviderConfiguration(NativeRuntimeEventSource.EventSourceName, (ulong)aggregatedKeywords, (uint)highestLevel, null)
+ };
+
+ m_sessionID = EventPipeInternal.Enable(null, EventPipeSerializationFormat.NetTrace, DefaultEventListenerCircularMBSize, providerConfiguration);
+ Debug.Assert(m_sessionID != 0);
+
+ // Get the session information that is required to properly dispatch events.
+ EventPipeSessionInfo sessionInfo;
+ unsafe
+ {
+ if (!EventPipeInternal.GetSessionInfo(m_sessionID, &sessionInfo))
+ {
+ Debug.Fail("GetSessionInfo returned false.");
+ }
+ }
+
+ m_syncTimeUtc = DateTime.FromFileTimeUtc(sessionInfo.StartTimeAsUTCFileTime);
+ m_syncTimeQPC = sessionInfo.StartTimeStamp;
+ m_timeQPCFrequency = sessionInfo.TimeStampFrequency;
+
+ // Start the dispatch task.
+ StartDispatchTask();
+ }
+
+ private void StartDispatchTask()
+ {
+ Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));
+
+ if (m_dispatchTask == null)
+ {
+ m_stopDispatchTask = false;
+ // Create a SafeWaitHandle that won't release the handle when done
+ m_dispatchTaskWaitHandle.SafeWaitHandle = new SafeWaitHandle(EventPipeInternal.GetWaitHandle(m_sessionID), false);
+
+ m_dispatchTask = Task.Factory.StartNew(DispatchEventsToEventListeners, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+ }
+ }
+
+ private void StopDispatchTask()
+ {
+ Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));
+
+ if (m_dispatchTask != null)
+ {
+ m_stopDispatchTask = true;
+ Debug.Assert(!m_dispatchTaskWaitHandle.SafeWaitHandle.IsInvalid);
+ EventWaitHandle.Set(m_dispatchTaskWaitHandle.SafeWaitHandle);
+ m_dispatchTask.Wait();
+ m_dispatchTask = null;
+ }
+ }
+
+ private unsafe void DispatchEventsToEventListeners()
+ {
+ // Struct to fill with the call to GetNextEvent.
+ EventPipeEventInstanceData instanceData;
+
+ while (!m_stopDispatchTask)
+ {
+ bool eventsReceived = false;
+ // Get the next event.
+ while (!m_stopDispatchTask && EventPipeInternal.GetNextEvent(m_sessionID, &instanceData))
+ {
+ eventsReceived = true;
+
+ // Filter based on provider.
+ if (instanceData.ProviderID == m_RuntimeProviderID)
+ {
+ // Dispatch the event.
+ ReadOnlySpan payload = new ReadOnlySpan((void*)instanceData.Payload, (int)instanceData.PayloadLength);
+ DateTime dateTimeStamp = TimeStampToDateTime(instanceData.TimeStamp);
+ NativeRuntimeEventSource.Log.ProcessEvent(instanceData.EventID, instanceData.ThreadID, dateTimeStamp, instanceData.ActivityId, instanceData.ChildActivityId, payload);
+ }
+ }
+
+ // Wait for more events.
+ if (!m_stopDispatchTask)
+ {
+ if (!eventsReceived)
+ {
+ // Future TODO: this would make more sense to handle in EventPipeSession/EventPipe native code.
+ Debug.Assert(!m_dispatchTaskWaitHandle.SafeWaitHandle.IsInvalid);
+ m_dispatchTaskWaitHandle.WaitOne();
+ }
+
+ Thread.Sleep(10);
+ }
+ }
+ }
+
+ ///
+ /// Converts a QueryPerformanceCounter (QPC) timestamp to a UTC DateTime.
+ ///
+ private DateTime TimeStampToDateTime(long timeStamp)
+ {
+ if (timeStamp == long.MaxValue)
+ {
+ return DateTime.MaxValue;
+ }
+
+ Debug.Assert((m_syncTimeUtc.Ticks != 0) && (m_syncTimeQPC != 0) && (m_timeQPCFrequency != 0));
+ long inTicks = (long)((timeStamp - m_syncTimeQPC) * 10000000.0 / m_timeQPCFrequency) + m_syncTimeUtc.Ticks;
+ if ((inTicks < 0) || (DateTime.MaxTicks < inTicks))
+ {
+ inTicks = DateTime.MaxTicks;
+ }
+
+ return new DateTime(inTicks, DateTimeKind.Utc);
+ }
+ }
+#endif // FEATURE_PERFTRACING
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipeEventProvider.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipeEventProvider.cs
new file mode 100644
index 000000000..34a7b861e
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipeEventProvider.cs
@@ -0,0 +1,89 @@
+// 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.
+
+namespace System.Diagnostics.Tracing
+{
+ internal sealed class EventPipeEventProvider : IEventProvider
+ {
+ // The EventPipeProvider handle.
+ private IntPtr m_provHandle = IntPtr.Zero;
+
+ // Register an event provider.
+ unsafe uint IEventProvider.EventRegister(
+ EventSource eventSource,
+ Interop.Advapi32.EtwEnableCallback enableCallback,
+ void* callbackContext,
+ ref long registrationHandle)
+ {
+ uint returnStatus = 0;
+ m_provHandle = EventPipeInternal.CreateProvider(eventSource.Name, enableCallback);
+ if (m_provHandle != IntPtr.Zero)
+ {
+ // Fixed registration handle because a new EventPipeEventProvider
+ // will be created for each new EventSource.
+ registrationHandle = 1;
+ }
+ else
+ {
+ // Unable to create the provider.
+ returnStatus = 1;
+ }
+
+ return returnStatus;
+ }
+
+ // Unregister an event provider.
+ uint IEventProvider.EventUnregister(long registrationHandle)
+ {
+ EventPipeInternal.DeleteProvider(m_provHandle);
+ return 0;
+ }
+
+ // Write an event.
+ unsafe EventProvider.WriteEventErrorCode IEventProvider.EventWriteTransfer(
+ long registrationHandle,
+ in EventDescriptor eventDescriptor,
+ IntPtr eventHandle,
+ Guid* activityId,
+ Guid* relatedActivityId,
+ int userDataCount,
+ EventProvider.EventData* userData)
+ {
+ if (eventHandle != IntPtr.Zero)
+ {
+ if (userDataCount == 0)
+ {
+ EventPipeInternal.WriteEventData(eventHandle, null, 0, activityId, relatedActivityId);
+ return EventProvider.WriteEventErrorCode.NoError;
+ }
+
+ // If Channel == 11, this is a TraceLogging event.
+ // The first 3 descriptors contain event metadata that is emitted for ETW and should be discarded on EventPipe.
+ // EventPipe metadata is provided via the EventPipeEventProvider.DefineEventHandle.
+ if (eventDescriptor.Channel == 11)
+ {
+ userData += 3;
+ userDataCount -= 3;
+ Debug.Assert(userDataCount >= 0);
+ }
+ EventPipeInternal.WriteEventData(eventHandle, userData, (uint)userDataCount, activityId, relatedActivityId);
+ }
+
+ return EventProvider.WriteEventErrorCode.NoError;
+ }
+
+ // Get or set the per-thread activity ID.
+ int IEventProvider.EventActivityIdControl(Interop.Advapi32.ActivityControl ControlCode, ref Guid ActivityId)
+ {
+ return EventPipeInternal.EventActivityIdControl((uint)ControlCode, ref ActivityId);
+ }
+
+ // Define an EventPipeEvent handle.
+ unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level, byte* pMetadata, uint metadataLength)
+ {
+ IntPtr eventHandlePtr = EventPipeInternal.DefineEvent(m_provHandle, eventID, keywords, eventVersion, level, pMetadata, metadataLength);
+ return eventHandlePtr;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipeMetadataGenerator.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipeMetadataGenerator.cs
new file mode 100644
index 000000000..fd81d0596
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipeMetadataGenerator.cs
@@ -0,0 +1,412 @@
+// 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.Reflection;
+using EventMetadata = System.Diagnostics.Tracing.EventSource.EventMetadata;
+
+namespace System.Diagnostics.Tracing
+{
+#if FEATURE_PERFTRACING
+ internal sealed class EventPipeMetadataGenerator
+ {
+ public static EventPipeMetadataGenerator Instance = new EventPipeMetadataGenerator();
+
+ private EventPipeMetadataGenerator() { }
+
+ public byte[]? GenerateEventMetadata(EventMetadata eventMetadata)
+ {
+ ParameterInfo[] parameters = eventMetadata.Parameters;
+ EventParameterInfo[] eventParams = new EventParameterInfo[parameters.Length];
+ for (int i = 0; i < parameters.Length; i++)
+ {
+ eventParams[i].SetInfo(parameters[i].Name!, parameters[i].ParameterType);
+ }
+
+ return GenerateMetadata(
+ eventMetadata.Descriptor.EventId,
+ eventMetadata.Name,
+ eventMetadata.Descriptor.Keywords,
+ eventMetadata.Descriptor.Level,
+ eventMetadata.Descriptor.Version,
+ eventParams);
+ }
+
+ public byte[]? GenerateEventMetadata(
+ int eventId,
+ string eventName,
+ EventKeywords keywords,
+ EventLevel level,
+ uint version,
+ TraceLoggingEventTypes eventTypes)
+ {
+ TraceLoggingTypeInfo[] typeInfos = eventTypes.typeInfos;
+ string[]? paramNames = eventTypes.paramNames;
+ EventParameterInfo[] eventParams = new EventParameterInfo[typeInfos.Length];
+ for (int i = 0; i < typeInfos.Length; i++)
+ {
+ string paramName = string.Empty;
+ if (paramNames != null)
+ {
+ paramName = paramNames[i];
+ }
+ eventParams[i].SetInfo(paramName, typeInfos[i].DataType, typeInfos[i]);
+ }
+
+ return GenerateMetadata(eventId, eventName, (long)keywords, (uint)level, version, eventParams);
+ }
+
+ internal unsafe byte[]? GenerateMetadata(
+ int eventId,
+ string eventName,
+ long keywords,
+ uint level,
+ uint version,
+ EventParameterInfo[] parameters)
+ {
+ byte[]? metadata = null;
+ try
+ {
+ // eventID : 4 bytes
+ // eventName : (eventName.Length + 1) * 2 bytes
+ // keywords : 8 bytes
+ // eventVersion : 4 bytes
+ // level : 4 bytes
+ // parameterCount : 4 bytes
+ uint metadataLength = 24 + ((uint)eventName.Length + 1) * 2;
+ uint defaultMetadataLength = metadataLength;
+
+ // Check for an empty payload.
+ // Write calls with no arguments by convention have a parameter of
+ // type NullTypeInfo which is serialized as nothing.
+ if ((parameters.Length == 1) && (parameters[0].ParameterType == typeof(EmptyStruct)))
+ {
+ parameters = Array.Empty();
+ }
+
+ // Increase the metadataLength for parameters.
+ foreach (EventParameterInfo parameter in parameters)
+ {
+ int pMetadataLength = parameter.GetMetadataLength();
+ // The call above may return -1 which means we failed to get the metadata length.
+ // We then return a default metadata blob (with parameterCount of 0) to prevent it from generating malformed metadata.
+ if (pMetadataLength < 0)
+ {
+ parameters = Array.Empty();
+ metadataLength = defaultMetadataLength;
+ break;
+ }
+ metadataLength += (uint)pMetadataLength;
+ }
+
+ metadata = new byte[metadataLength];
+
+ // Write metadata: eventID, eventName, keywords, eventVersion, level, parameterCount, param1 type, param1 name...
+ fixed (byte* pMetadata = metadata)
+ {
+ uint offset = 0;
+ WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)eventId);
+ fixed (char* pEventName = eventName)
+ {
+ WriteToBuffer(pMetadata, metadataLength, ref offset, (byte*)pEventName, ((uint)eventName.Length + 1) * 2);
+ }
+ WriteToBuffer(pMetadata, metadataLength, ref offset, keywords);
+ WriteToBuffer(pMetadata, metadataLength, ref offset, version);
+ WriteToBuffer(pMetadata, metadataLength, ref offset, level);
+ WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)parameters.Length);
+ foreach (EventParameterInfo parameter in parameters)
+ {
+ if (!parameter.GenerateMetadata(pMetadata, ref offset, metadataLength))
+ {
+ // If we fail to generate metadata for any parameter, we should return the "default" metadata without any parameters
+ return GenerateMetadata(eventId, eventName, keywords, level, version, Array.Empty());
+ }
+ }
+ Debug.Assert(metadataLength == offset);
+ }
+ }
+ catch
+ {
+ // If a failure occurs during metadata generation, make sure that we don't return
+ // malformed metadata. Instead, return a null metadata blob.
+ // Consumers can either build in knowledge of the event or skip it entirely.
+ metadata = null;
+ }
+
+ return metadata;
+ }
+
+ // Copy src to buffer and modify the offset.
+ // Note: We know the buffer size ahead of time to make sure no buffer overflow.
+ internal static unsafe void WriteToBuffer(byte* buffer, uint bufferLength, ref uint offset, byte* src, uint srcLength)
+ {
+ Debug.Assert(bufferLength >= (offset + srcLength));
+ for (int i = 0; i < srcLength; i++)
+ {
+ *(byte*)(buffer + offset + i) = *(byte*)(src + i);
+ }
+ offset += srcLength;
+ }
+
+ // Copy uint value to buffer.
+ internal static unsafe void WriteToBuffer(byte* buffer, uint bufferLength, ref uint offset, uint value)
+ {
+ Debug.Assert(bufferLength >= (offset + 4));
+ *(uint*)(buffer + offset) = value;
+ offset += 4;
+ }
+
+ // Copy long value to buffer.
+ internal static unsafe void WriteToBuffer(byte* buffer, uint bufferLength, ref uint offset, long value)
+ {
+ Debug.Assert(bufferLength >= (offset + 8));
+ *(long*)(buffer + offset) = value;
+ offset += 8;
+ }
+
+ // Copy char value to buffer.
+ internal static unsafe void WriteToBuffer(byte* buffer, uint bufferLength, ref uint offset, char value)
+ {
+ Debug.Assert(bufferLength >= (offset + 2));
+ *(char*)(buffer + offset) = value;
+ offset += 2;
+ }
+ }
+
+ internal struct EventParameterInfo
+ {
+ internal string ParameterName;
+ internal Type ParameterType;
+ internal TraceLoggingTypeInfo? TypeInfo;
+
+ internal void SetInfo(string name, Type type, TraceLoggingTypeInfo? typeInfo = null)
+ {
+ ParameterName = name;
+ ParameterType = type;
+ TypeInfo = typeInfo;
+ }
+
+ internal unsafe bool GenerateMetadata(byte* pMetadataBlob, ref uint offset, uint blobSize)
+ {
+ TypeCode typeCode = GetTypeCodeExtended(ParameterType);
+ if (typeCode == TypeCode.Object)
+ {
+ // Each nested struct is serialized as:
+ // TypeCode.Object : 4 bytes
+ // Number of properties : 4 bytes
+ // Property description 0...N
+ // Nested struct property name : NULL-terminated string.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)TypeCode.Object);
+
+ if (!(TypeInfo is InvokeTypeInfo invokeTypeInfo))
+ {
+ return false;
+ }
+
+ // Get the set of properties to be serialized.
+ PropertyAnalysis[]? properties = invokeTypeInfo.properties;
+ if (properties != null)
+ {
+ // Write the count of serializable properties.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)properties.Length);
+
+ foreach (PropertyAnalysis prop in properties)
+ {
+ if (!GenerateMetadataForProperty(prop, pMetadataBlob, ref offset, blobSize))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // This struct has zero serializable properties so we just write the property count.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)0);
+ }
+
+ // Top-level structs don't have a property name, but for simplicity we write a NULL-char to represent the name.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, '\0');
+ }
+ else
+ {
+ // Write parameter type.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)typeCode);
+
+ // Write parameter name.
+ fixed (char* pParameterName = ParameterName)
+ {
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (byte*)pParameterName, ((uint)ParameterName.Length + 1) * 2);
+ }
+ }
+ return true;
+ }
+
+ private static unsafe bool GenerateMetadataForProperty(PropertyAnalysis property, byte* pMetadataBlob, ref uint offset, uint blobSize)
+ {
+ Debug.Assert(property != null);
+ Debug.Assert(pMetadataBlob != null);
+
+ // Check if this property is a nested struct.
+ if (property.typeInfo is InvokeTypeInfo invokeTypeInfo)
+ {
+ // Each nested struct is serialized as:
+ // TypeCode.Object : 4 bytes
+ // Number of properties : 4 bytes
+ // Property description 0...N
+ // Nested struct property name : NULL-terminated string.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)TypeCode.Object);
+
+ // Get the set of properties to be serialized.
+ PropertyAnalysis[]? properties = invokeTypeInfo.properties;
+ if (properties != null)
+ {
+ // Write the count of serializable properties.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)properties.Length);
+
+ foreach (PropertyAnalysis prop in properties)
+ {
+ if (!GenerateMetadataForProperty(prop, pMetadataBlob, ref offset, blobSize))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // This struct has zero serializable properties so we just write the property count.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)0);
+ }
+
+ // Write the property name.
+ fixed (char* pPropertyName = property.name)
+ {
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (byte*)pPropertyName, ((uint)property.name.Length + 1) * 2);
+ }
+ }
+ else
+ {
+ // Each primitive type is serialized as:
+ // TypeCode : 4 bytes
+ // PropertyName : NULL-terminated string
+ TypeCode typeCode = GetTypeCodeExtended(property.typeInfo.DataType);
+
+ // EventPipe does not support this type. Throw, which will cause no metadata to be registered for this event.
+ if (typeCode == TypeCode.Object)
+ {
+ return false;
+ }
+
+ // Write the type code.
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (uint)typeCode);
+
+ // Write the property name.
+ fixed (char* pPropertyName = property.name)
+ {
+ EventPipeMetadataGenerator.WriteToBuffer(pMetadataBlob, blobSize, ref offset, (byte*)pPropertyName, ((uint)property.name.Length + 1) * 2);
+ }
+ }
+ return true;
+ }
+
+ internal int GetMetadataLength()
+ {
+ int ret = 0;
+
+ TypeCode typeCode = GetTypeCodeExtended(ParameterType);
+ if (typeCode == TypeCode.Object)
+ {
+ if (!(TypeInfo is InvokeTypeInfo typeInfo))
+ {
+ return -1;
+ }
+
+ // Each nested struct is serialized as:
+ // TypeCode.Object : 4 bytes
+ // Number of properties : 4 bytes
+ // Property description 0...N
+ // Nested struct property name : NULL-terminated string.
+ ret += sizeof(uint) // TypeCode
+ + sizeof(uint); // Property count
+
+ // Get the set of properties to be serialized.
+ PropertyAnalysis[]? properties = typeInfo.properties;
+ if (properties != null)
+ {
+ foreach (PropertyAnalysis prop in properties)
+ {
+ ret += (int)GetMetadataLengthForProperty(prop);
+ }
+ }
+
+ // For simplicity when writing a reader, we write a NULL char
+ // after the metadata for a top-level struct (for its name) so that
+ // readers don't have do special case the outer-most struct.
+ ret += sizeof(char);
+ }
+ else
+ {
+ ret += (int)(sizeof(uint) + ((ParameterName.Length + 1) * 2));
+ }
+
+ return ret;
+ }
+
+ private static uint GetMetadataLengthForProperty(PropertyAnalysis property)
+ {
+ Debug.Assert(property != null);
+
+ uint ret = 0;
+
+ // Check if this property is a nested struct.
+ if (property.typeInfo is InvokeTypeInfo invokeTypeInfo)
+ {
+ // Each nested struct is serialized as:
+ // TypeCode.Object : 4 bytes
+ // Number of properties : 4 bytes
+ // Property description 0...N
+ // Nested struct property name : NULL-terminated string.
+ ret += sizeof(uint) // TypeCode
+ + sizeof(uint); // Property count
+
+ // Get the set of properties to be serialized.
+ PropertyAnalysis[]? properties = invokeTypeInfo.properties;
+ if (properties != null)
+ {
+ foreach (PropertyAnalysis prop in properties)
+ {
+ ret += GetMetadataLengthForProperty(prop);
+ }
+ }
+
+ // Add the size of the property name.
+ ret += (uint)((property.name.Length + 1) * 2);
+ }
+ else
+ {
+ ret += (uint)(sizeof(uint) + ((property.name.Length + 1) * 2));
+ }
+
+ return ret;
+ }
+
+ private static TypeCode GetTypeCodeExtended(Type parameterType)
+ {
+ // Guid is not part of TypeCode, we decided to use 17 to represent it, as it's the "free slot"
+ // see https://github.com/dotnet/coreclr/issues/16105#issuecomment-361749750 for more
+ const TypeCode GuidTypeCode = (TypeCode)17;
+
+ if (parameterType == typeof(Guid)) // Guid is not a part of TypeCode enum
+ return GuidTypeCode;
+
+ // IntPtr and UIntPtr are converted to their non-pointer types.
+ if (parameterType == typeof(IntPtr))
+ return IntPtr.Size == 4 ? TypeCode.Int32 : TypeCode.Int64;
+
+ if (parameterType == typeof(UIntPtr))
+ return UIntPtr.Size == 4 ? TypeCode.UInt32 : TypeCode.UInt64;
+
+ return Type.GetTypeCode(parameterType);
+ }
+ }
+
+#endif // FEATURE_PERFTRACING
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipePayloadDecoder.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipePayloadDecoder.cs
new file mode 100644
index 000000000..6d5441832
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventPipePayloadDecoder.cs
@@ -0,0 +1,149 @@
+// 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.Buffers.Binary;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace System.Diagnostics.Tracing
+{
+#if FEATURE_PERFTRACING
+ internal static class EventPipePayloadDecoder
+ {
+ ///
+ /// Given the metadata for an event and an event payload, decode and deserialize the event payload.
+ ///
+ internal static object[] DecodePayload(ref EventSource.EventMetadata metadata, ReadOnlySpan payload)
+ {
+ ParameterInfo[] parameters = metadata.Parameters;
+ object[] decodedFields = new object[parameters.Length];
+ for (int i = 0; i < parameters.Length; i++)
+ {
+ // It is possible that an older version of the event was emitted.
+ // If this happens, the payload might be missing arguments at the end.
+ // We can just leave these unset.
+ if (payload.Length <= 0)
+ {
+ break;
+ }
+
+ Type parameterType = parameters[i].ParameterType;
+ if (parameterType == typeof(IntPtr))
+ {
+ if (IntPtr.Size == 8)
+ {
+ decodedFields[i] = (IntPtr)BinaryPrimitives.ReadInt64LittleEndian(payload);
+ }
+ else
+ {
+ decodedFields[i] = (IntPtr)BinaryPrimitives.ReadInt32LittleEndian(payload);
+ }
+ payload = payload.Slice(IntPtr.Size);
+ }
+ else if (parameterType == typeof(int))
+ {
+ decodedFields[i] = BinaryPrimitives.ReadInt32LittleEndian(payload);
+ payload = payload.Slice(sizeof(int));
+ }
+ else if (parameterType == typeof(uint))
+ {
+ decodedFields[i] = BinaryPrimitives.ReadUInt32LittleEndian(payload);
+ payload = payload.Slice(sizeof(uint));
+ }
+ else if (parameterType == typeof(long))
+ {
+ decodedFields[i] = BinaryPrimitives.ReadInt64LittleEndian(payload);
+ payload = payload.Slice(sizeof(long));
+ }
+ else if (parameterType == typeof(ulong))
+ {
+ decodedFields[i] = BinaryPrimitives.ReadUInt64LittleEndian(payload);
+ payload = payload.Slice(sizeof(ulong));
+ }
+ else if (parameterType == typeof(byte))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(byte));
+ }
+ else if (parameterType == typeof(sbyte))
+ {
+ decodedFields[i] = MemoryMarshal.Read(payload);
+ payload = payload.Slice(sizeof(sbyte));
+ }
+ else if (parameterType == typeof(short))
+ {
+ decodedFields[i] = BinaryPrimitives.ReadInt16LittleEndian(payload);
+ payload = payload.Slice(sizeof(short));
+ }
+ else if (parameterType == typeof(ushort))
+ {
+ decodedFields[i] = BinaryPrimitives.ReadUInt16LittleEndian(payload);
+ payload = payload.Slice(sizeof(ushort));
+ }
+ else if (parameterType == typeof(float))
+ {
+ decodedFields[i] = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(payload));
+ payload = payload.Slice(sizeof(float));
+ }
+ else if (parameterType == typeof(double))
+ {
+ decodedFields[i] = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(payload));
+ payload = payload.Slice(sizeof(double));
+ }
+ else if (parameterType == typeof(bool))
+ {
+ // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
+ decodedFields[i] = (BinaryPrimitives.ReadInt32LittleEndian(payload) == 1);
+ payload = payload.Slice(sizeof(int));
+ }
+ else if (parameterType == typeof(Guid))
+ {
+ const int sizeOfGuid = 16;
+ decodedFields[i] = new Guid(payload.Slice(0, sizeOfGuid));
+ payload = payload.Slice(sizeOfGuid);
+ }
+ else if (parameterType == typeof(char))
+ {
+ decodedFields[i] = (char)BinaryPrimitives.ReadUInt16LittleEndian(payload);
+ payload = payload.Slice(sizeof(char));
+ }
+ else if (parameterType == typeof(string))
+ {
+ // Try to find null terminator (0x00) from the byte span
+ // NOTE: we do this by hand instead of using IndexOf because payload may be unaligned due to
+ // mixture of different types being stored in the same buffer. (see eventpipe.cpp:CopyData)
+ int byteCount = -1;
+ for (int j = 1; j < payload.Length; j += 2)
+ {
+ if (payload[j - 1] == (byte)(0) && payload[j] == (byte)(0))
+ {
+ byteCount = j + 1;
+ break;
+ }
+ }
+
+ ReadOnlySpan charPayload;
+ if (byteCount < 0)
+ {
+ charPayload = MemoryMarshal.Cast(payload);
+ payload = default;
+ }
+ else
+ {
+ charPayload = MemoryMarshal.Cast(payload.Slice(0, byteCount - 2));
+ payload = payload.Slice(byteCount);
+ }
+ decodedFields[i] = BitConverter.IsLittleEndian ? new string(charPayload) : Encoding.Unicode.GetString(MemoryMarshal.Cast(charPayload));
+ }
+ else
+ {
+ Debug.Fail("Unsupported type encountered.");
+ }
+ }
+
+ return decodedFields;
+ }
+ }
+#endif // FEATURE_PERFTRACING
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs
index b0ed1c98c..fd0bd6eab 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs
@@ -13,7 +13,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
-#if CORECLR && TARGET_WINDOWS
+#if (CORECLR || MONO) && TARGET_WINDOWS
using Internal.Win32;
#endif
#if ES_BUILD_AGAINST_DOTNET_V35
@@ -288,10 +288,8 @@ namespace System.Diagnostics.Tracing
filterData = null;
// read filter data only when a session is being *added*
- byte[]? data;
- int keyIndex;
if (bEnabling &&
- GetDataFromController(etwSessionId, filterData, out command, out data, out keyIndex))
+ GetDataFromController(etwSessionId, filterData, out command, out byte[]? data, out int keyIndex))
{
args = new Dictionary(4);
// data can be null if the filterArgs had a very large size which failed our sanity check
@@ -469,42 +467,59 @@ namespace System.Diagnostics.Tracing
// does not have this issue.
#if (TARGET_WINDOWS && (ES_SESSION_INFO || !ES_BUILD_STANDALONE))
int buffSize = 256; // An initial guess that probably works most of the time.
- byte* buffer;
- while (true)
+ byte* stackSpace = stackalloc byte[buffSize];
+ byte* buffer = stackSpace;
+ try
{
- byte* space = stackalloc byte[buffSize];
- buffer = space;
- int hr = 0;
-
- fixed (Guid* provider = &m_providerId)
+ while (true)
{
- hr = Interop.Advapi32.EnumerateTraceGuidsEx(Interop.Advapi32.TRACE_QUERY_INFO_CLASS.TraceGuidQueryInfo,
- provider, sizeof(Guid), buffer, buffSize, out buffSize);
+ int hr = 0;
+
+ fixed (Guid* provider = &m_providerId)
+ {
+ hr = Interop.Advapi32.EnumerateTraceGuidsEx(Interop.Advapi32.TRACE_QUERY_INFO_CLASS.TraceGuidQueryInfo,
+ provider, sizeof(Guid), buffer, buffSize, out buffSize);
+ }
+ if (hr == 0)
+ break;
+ if (hr != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
+ return;
+
+ if (buffer != stackSpace)
+ {
+ byte* toFree = buffer;
+ buffer = null;
+ Marshal.FreeHGlobal((IntPtr)toFree);
+ }
+ buffer = (byte*)Marshal.AllocHGlobal(buffSize);
+ }
+
+ var providerInfos = (Interop.Advapi32.TRACE_GUID_INFO*)buffer;
+ var providerInstance = (Interop.Advapi32.TRACE_PROVIDER_INSTANCE_INFO*)&providerInfos[1];
+ int processId = unchecked((int)Interop.Kernel32.GetCurrentProcessId());
+ // iterate over the instances of the EventProvider in all processes
+ for (int i = 0; i < providerInfos->InstanceCount; i++)
+ {
+ if (providerInstance->Pid == processId)
+ {
+ var enabledInfos = (Interop.Advapi32.TRACE_ENABLE_INFO*)&providerInstance[1];
+ // iterate over the list of active ETW sessions "listening" to the current provider
+ for (int j = 0; j < providerInstance->EnableCount; j++)
+ action(enabledInfos[j].LoggerId, enabledInfos[j].MatchAllKeyword, ref sessionList);
+ }
+ if (providerInstance->NextOffset == 0)
+ break;
+ Debug.Assert(0 <= providerInstance->NextOffset && providerInstance->NextOffset < buffSize);
+ byte* structBase = (byte*)providerInstance;
+ providerInstance = (Interop.Advapi32.TRACE_PROVIDER_INSTANCE_INFO*)&structBase[providerInstance->NextOffset];
}
- if (hr == 0)
- break;
- if (hr != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
- return;
}
-
- var providerInfos = (Interop.Advapi32.TRACE_GUID_INFO*)buffer;
- var providerInstance = (Interop.Advapi32.TRACE_PROVIDER_INSTANCE_INFO*)&providerInfos[1];
- int processId = unchecked((int)Interop.Kernel32.GetCurrentProcessId());
- // iterate over the instances of the EventProvider in all processes
- for (int i = 0; i < providerInfos->InstanceCount; i++)
+ finally
{
- if (providerInstance->Pid == processId)
+ if (buffer != null && buffer != stackSpace)
{
- var enabledInfos = (Interop.Advapi32.TRACE_ENABLE_INFO*)&providerInstance[1];
- // iterate over the list of active ETW sessions "listening" to the current provider
- for (int j = 0; j < providerInstance->EnableCount; j++)
- action(enabledInfos[j].LoggerId, enabledInfos[j].MatchAllKeyword, ref sessionList);
+ Marshal.FreeHGlobal((IntPtr)buffer);
}
- if (providerInstance->NextOffset == 0)
- break;
- Debug.Assert(0 <= providerInstance->NextOffset && providerInstance->NextOffset < buffSize);
- byte* structBase = (byte*)providerInstance;
- providerInstance = (Interop.Advapi32.TRACE_PROVIDER_INSTANCE_INFO*)&structBase[providerInstance->NextOffset];
}
#else
#if !ES_BUILD_PCL && TARGET_WINDOWS // TODO command arguments don't work on PCL builds...
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
index 9e08f5bec..fd42fb69b 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
@@ -649,10 +649,8 @@ namespace System.Diagnostics.Tracing
{
m_config = ValidateSettings(settings);
- Guid eventSourceGuid;
- string? eventSourceName;
- GetMetadata(out eventSourceGuid, out eventSourceName, out _, out _);
+ GetMetadata(out Guid eventSourceGuid, out string? eventSourceName, out _, out _);
if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null)
{
@@ -1373,28 +1371,29 @@ namespace System.Diagnostics.Tracing
int dataCount,
IntPtr data)
{
+#if FEATURE_MANAGED_ETW || FEATURE_PERFTRACING
+ bool allAreNull = true;
#if FEATURE_MANAGED_ETW
- if (m_etwProvider == null)
+ allAreNull &= (m_etwProvider == null);
+ if (m_etwProvider != null
+ && !m_etwProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
{
ThrowEventSourceException(eventName);
}
- else
- {
- if (!m_etwProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
- ThrowEventSourceException(eventName);
- }
#endif // FEATURE_MANAGED_ETW
#if FEATURE_PERFTRACING
- if (m_eventPipeProvider == null)
+ allAreNull &= (m_eventPipeProvider == null);
+ if (m_eventPipeProvider != null
+ && !m_eventPipeProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
{
ThrowEventSourceException(eventName);
}
- else
- {
- if (!m_eventPipeProvider.WriteEventRaw(ref eventDescriptor, eventHandle, activityID, relatedActivityID, dataCount, data))
- ThrowEventSourceException(eventName);
- }
#endif // FEATURE_PERFTRACING
+ if (allAreNull)
+ {
+ ThrowEventSourceException(eventName);
+ }
+#endif // FEATURE_MANAGED_ETW || FEATURE_PERFTRACING
}
// FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
@@ -2150,49 +2149,95 @@ namespace System.Diagnostics.Tracing
}
}
- private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
+ // WriteEventString is used for logging an error message (or similar) to
+ // ETW and EventPipe providers. It is not a general purpose API, it will
+ // log the message with Level=LogAlways and Keywords=All to make sure whoever
+ // is listening gets the message.
+ private unsafe void WriteEventString(string msgString)
{
+#if FEATURE_MANAGED_ETW || FEATURE_PERFTRACING
+ bool allAreNull = true;
#if FEATURE_MANAGED_ETW
- if (m_etwProvider != null)
+ allAreNull &= (m_etwProvider == null);
+#endif // FEATURE_MANAGED_ETW
+#if FEATURE_PERFTRACING
+ allAreNull &= (m_eventPipeProvider == null);
+#endif // FEATURE_PERFTRACING
+ if (allAreNull)
{
- const string EventName = "EventSourceMessage";
- if (SelfDescribingEvents)
- {
- EventSourceOptions opt = new EventSourceOptions
- {
- Keywords = (EventKeywords)unchecked(keywords),
- Level = level
- };
- var msg = new { message = msgString };
- var tlet = new TraceLoggingEventTypes(EventName, EventTags.None, new Type[] { msg.GetType() });
- WriteMultiMergeInner(EventName, ref opt, tlet, null, null, msg);
- }
- else
- {
- // We want the name of the provider to show up so if we don't have a manifest we create
- // on that at least has the provider name (I don't define any events).
- if (m_rawManifest == null && m_outOfBandMessageCount == 1)
- {
- ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
- manifestBuilder.StartEvent(EventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
- manifestBuilder.AddEventParameter(typeof(string), "message");
- manifestBuilder.EndEvent();
- SendManifest(manifestBuilder.CreateManifest());
- }
+ return;
+ }
- // We use this low level routine to bypass the enabled checking, since the eventSource itself is only partially inited.
- fixed (char* msgStringPtr = msgString)
+ EventLevel level = EventLevel.LogAlways;
+ long keywords = -1;
+ const string EventName = "EventSourceMessage";
+ if (SelfDescribingEvents)
+ {
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)unchecked(keywords),
+ Level = level
+ };
+ var msg = new { message = msgString };
+ var tlet = new TraceLoggingEventTypes(EventName, EventTags.None, new Type[] { msg.GetType() });
+ WriteMultiMergeInner(EventName, ref opt, tlet, null, null, msg);
+ }
+ else
+ {
+ // We want the name of the provider to show up so if we don't have a manifest we create
+ // on that at least has the provider name (I don't define any events).
+ if (m_rawManifest == null && m_outOfBandMessageCount == 1)
+ {
+ ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
+ manifestBuilder.StartEvent(EventName, new EventAttribute(0) { Level = level, Task = (EventTask)0xFFFE });
+ manifestBuilder.AddEventParameter(typeof(string), "message");
+ manifestBuilder.EndEvent();
+ SendManifest(manifestBuilder.CreateManifest());
+ }
+
+ // We use this low level routine to bypass the enabled checking, since the eventSource itself is only partially inited.
+ fixed (char* msgStringPtr = msgString)
+ {
+ EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
+ EventProvider.EventData data = default;
+ data.Ptr = (ulong)msgStringPtr;
+ data.Size = (uint)(2 * (msgString.Length + 1));
+ data.Reserved = 0;
+#if FEATURE_MANAGED_ETW
+ if (m_etwProvider != null)
{
- EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
- EventProvider.EventData data = default;
- data.Ptr = (ulong)msgStringPtr;
- data.Size = (uint)(2 * (msgString.Length + 1));
- data.Reserved = 0;
m_etwProvider.WriteEvent(ref descr, IntPtr.Zero, null, null, 1, (IntPtr)((void*)&data));
}
+#endif // FEATURE_MANAGED_ETW
+#if FEATURE_PERFTRACING
+ if (m_eventPipeProvider != null)
+ {
+ if (m_writeEventStringEventHandle == IntPtr.Zero)
+ {
+ lock (m_createEventLock)
+ {
+ if (m_writeEventStringEventHandle == IntPtr.Zero)
+ {
+ string eventName = "EventSourceMessage";
+ EventParameterInfo paramInfo = default(EventParameterInfo);
+ paramInfo.SetInfo("message", typeof(string));
+ byte[]? metadata = EventPipeMetadataGenerator.Instance.GenerateMetadata(0, eventName, keywords, (uint)level, 0, new EventParameterInfo[] { paramInfo });
+ uint metadataLength = (metadata != null) ? (uint)metadata.Length : 0;
+
+ fixed (byte* pMetadata = metadata)
+ {
+ m_writeEventStringEventHandle = m_eventPipeProvider.m_eventProvider.DefineEventHandle(0, eventName, keywords, 0, (uint)level, pMetadata, metadataLength);
+ }
+ }
+ }
+ }
+
+ m_eventPipeProvider.WriteEvent(ref descr, m_writeEventStringEventHandle, null, null, 1, (IntPtr)((void*)&data));
+ }
+#endif // FEATURE_PERFTRACING
}
}
-#endif // FEATURE_MANAGED_ETW
+#endif // FEATURE_MANAGED_ETW || FEATURE_PERFTRACING
}
///
@@ -2871,13 +2916,10 @@ namespace System.Diagnostics.Tracing
if (m_eventData == null)
{
Guid eventSourceGuid = Guid.Empty;
- string? eventSourceName = null;
- EventMetadata[]? eventData = null;
- byte[]? manifest = null;
// Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back
// to the reflection approach.
- GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest);
+ GetMetadata(out eventSourceGuid, out string? eventSourceName, out EventMetadata[]? eventData, out byte[]? manifest);
if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null)
{
@@ -3808,7 +3850,7 @@ namespace System.Diagnostics.Tracing
msg = "Reached message limit. End of EventSource error messages.";
}
- WriteEventString(EventLevel.LogAlways, -1, msg);
+ WriteEventString(msg);
WriteStringToAllListeners("EventSourceMessage", msg);
}
catch { } // If we fail during last chance logging, well, we have to give up....
@@ -3865,6 +3907,8 @@ namespace System.Diagnostics.Tracing
private volatile OverideEventProvider m_etwProvider = null!; // This hooks up ETW commands to our 'OnEventCommand' callback
#endif
#if FEATURE_PERFTRACING
+ private object m_createEventLock = new object();
+ private IntPtr m_writeEventStringEventHandle = IntPtr.Zero;
private volatile OverideEventProvider m_eventPipeProvider = null!;
#endif
private bool m_completelyInited; // The EventSource constructor has returned without exception.
@@ -5551,8 +5595,7 @@ namespace System.Diagnostics.Tracing
if (channelTab.Count == MaxCountChannels)
ManifestError(SR.EventSource_MaxChannelExceeded);
- ChannelInfo? info;
- if (!channelTab.TryGetValue((int)channel, out info))
+ if (!channelTab.TryGetValue((int)channel, out ChannelInfo? info))
{
// If we were not given an explicit channel, allocate one.
if (channelKeyword != 0)
@@ -5876,8 +5919,7 @@ namespace System.Diagnostics.Tracing
#if FEATURE_MANAGED_ETW_CHANNELS
private string? GetChannelName(EventChannel channel, string eventName, string? eventMessage)
{
- ChannelInfo? info = null;
- if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
+ if (channelTab == null || !channelTab.TryGetValue((int)channel, out ChannelInfo? info))
{
if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName));
@@ -5909,9 +5951,8 @@ namespace System.Diagnostics.Tracing
if (task == EventTask.None)
return "";
- string? ret;
taskTab ??= new Dictionary();
- if (!taskTab.TryGetValue((int)task, out ret))
+ if (!taskTab.TryGetValue((int)task, out string? ret))
ret = taskTab[(int)task] = eventName;
return ret;
}
@@ -5944,8 +5985,7 @@ namespace System.Diagnostics.Tracing
return "win:Receive";
}
- string? ret;
- if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
+ if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out string? ret))
{
ManifestError(SR.Format(SR.EventSource_UndefinedOpcode, opcode, eventName), true);
ret = null;
@@ -6052,7 +6092,6 @@ namespace System.Diagnostics.Tracing
{
StringBuilder? stringBuilder = null; // We lazily create this
int writtenSoFar = 0;
- int chIdx = -1;
for (int i = 0; ;)
{
if (i >= eventMessage.Length)
@@ -6063,6 +6102,7 @@ namespace System.Diagnostics.Tracing
return stringBuilder.ToString();
}
+ int chIdx;
if (eventMessage[i] == '%')
{
// handle format message escaping character '%' by escaping it
@@ -6125,8 +6165,7 @@ namespace System.Diagnostics.Tracing
private int TranslateIndexToManifestConvention(int idx, string evtName)
{
- List? byteArrArgIndices;
- if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
+ if (perEventByteArrayArgIndices.TryGetValue(evtName, out List? byteArrArgIndices))
{
foreach (int byArrIdx in byteArrArgIndices)
{
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs
new file mode 100644
index 000000000..5272f5077
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs
@@ -0,0 +1,49 @@
+// 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.
+
+namespace System.Diagnostics.Tracing
+{
+ ///
+ /// NativeRuntimeEventSource is an EventSource that represents the ETW/EventPipe events emitted by the native runtime.
+ /// Most of NativeRuntimeEventSource is auto-generated by scripts/genRuntimeEventSources.py based on the contents of the Microsoft-Windows-DotNETRuntime provider.
+ ///
+ [EventSource(Guid = "5E5BB766-BBFC-5662-0548-1D44FAD9BB56", Name = "Microsoft-Windows-DotNETRuntime")]
+ internal sealed partial class NativeRuntimeEventSource : EventSource
+ {
+ internal const string EventSourceName = "Microsoft-Windows-DotNETRuntime";
+ internal static NativeRuntimeEventSource Log = new NativeRuntimeEventSource();
+
+ // The NativeRuntimeEventSource GUID is {5e5bb766-bbfc-5662-0548-1d44fad9bb56}
+ private NativeRuntimeEventSource() : base(new Guid(0x5e5bb766, 0xbbfc, 0x5662, 0x05, 0x48, 0x1d, 0x44, 0xfa, 0xd9, 0xbb, 0x56), EventSourceName) { }
+
+ ///
+ /// Dispatch a single event with the specified event ID and payload.
+ ///
+ /// The eventID corresponding to the event as defined in the auto-generated portion of the NativeRuntimeEventSource class.
+ /// The thread ID of the operating system thread.
+ /// The current timestamp.
+ /// The ID of the current activity.
+ /// The ID of the current child activity.
+ /// A span pointing to the data payload for the event.
+ [NonEvent]
+ internal unsafe void ProcessEvent(uint eventID, uint osThreadID, DateTime timeStamp, Guid activityId, Guid childActivityId, ReadOnlySpan payload)
+ {
+ // Make sure the eventID is valid.
+ if (eventID >= m_eventData!.Length)
+ {
+ return;
+ }
+
+ // Decode the payload.
+ object[] decodedPayloadFields = EventPipePayloadDecoder.DecodePayload(ref m_eventData[eventID], payload);
+ WriteToAllListeners(
+ eventId: (int)eventID,
+ osThreadId: &osThreadID,
+ timeStamp: &timeStamp,
+ activityID: &activityId,
+ childActivityID: &childActivityId,
+ args: decodedPayloadFields);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/RuntimeEventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/RuntimeEventSource.cs
new file mode 100644
index 000000000..336e981bd
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/RuntimeEventSource.cs
@@ -0,0 +1,85 @@
+// 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;
+
+namespace System.Diagnostics.Tracing
+{
+ ///
+ /// RuntimeEventSource is an EventSource that represents events emitted by the managed runtime.
+ ///
+ [EventSource(Guid = "49592C0F-5A05-516D-AA4B-A64E02026C89", Name = "System.Runtime")]
+ internal sealed class RuntimeEventSource : EventSource
+ {
+ internal const string EventSourceName = "System.Runtime";
+
+ private static RuntimeEventSource? s_RuntimeEventSource;
+ private PollingCounter? _gcHeapSizeCounter;
+ private IncrementingPollingCounter? _gen0GCCounter;
+ private IncrementingPollingCounter? _gen1GCCounter;
+ private IncrementingPollingCounter? _gen2GCCounter;
+ private PollingCounter? _cpuTimeCounter;
+ private PollingCounter? _workingSetCounter;
+ private PollingCounter? _threadPoolThreadCounter;
+ private IncrementingPollingCounter? _monitorContentionCounter;
+ private PollingCounter? _threadPoolQueueCounter;
+ private IncrementingPollingCounter? _completedItemsCounter;
+ private IncrementingPollingCounter? _allocRateCounter;
+ private PollingCounter? _timerCounter;
+
+#if !MONO
+ private IncrementingPollingCounter? _exceptionCounter;
+ private PollingCounter? _gcTimeCounter;
+ private PollingCounter? _gen0SizeCounter;
+ private PollingCounter? _gen1SizeCounter;
+ private PollingCounter? _gen2SizeCounter;
+ private PollingCounter? _lohSizeCounter;
+ private PollingCounter? _assemblyCounter;
+#endif
+
+ public static void Initialize()
+ {
+ s_RuntimeEventSource = new RuntimeEventSource();
+ }
+
+ private RuntimeEventSource() : base(new Guid(0x49592C0F, 0x5A05, 0x516D, 0xAA, 0x4B, 0xA6, 0x4E, 0x02, 0x02, 0x6C, 0x89), EventSourceName, EventSourceSettings.EtwSelfDescribingEventFormat)
+ {
+ }
+
+ protected override void OnEventCommand(EventCommandEventArgs command)
+ {
+ if (command.Command == EventCommand.Enable)
+ {
+ // NOTE: These counters will NOT be disposed on disable command because we may be introducing
+ // a race condition by doing that. We still want to create these lazily so that we aren't adding
+ // overhead by at all times even when counters aren't enabled.
+
+ // On disable, PollingCounters will stop polling for values so it should be fine to leave them around.
+ _cpuTimeCounter ??= new PollingCounter("cpu-usage", this, () => RuntimeEventSourceHelper.GetCpuUsage()) { DisplayName = "CPU Usage", DisplayUnits = "%" };
+ _workingSetCounter ??= new PollingCounter("working-set", this, () => (double)(Environment.WorkingSet / 1000000)) { DisplayName = "Working Set", DisplayUnits = "MB" };
+ _gcHeapSizeCounter ??= new PollingCounter("gc-heap-size", this, () => (double)(GC.GetTotalMemory(false) / 1000000)) { DisplayName = "GC Heap Size", DisplayUnits = "MB" };
+ _gen0GCCounter ??= new IncrementingPollingCounter("gen-0-gc-count", this, () => GC.CollectionCount(0)) { DisplayName = "Gen 0 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) };
+ _gen1GCCounter ??= new IncrementingPollingCounter("gen-1-gc-count", this, () => GC.CollectionCount(1)) { DisplayName = "Gen 1 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) };
+ _gen2GCCounter ??= new IncrementingPollingCounter("gen-2-gc-count", this, () => GC.CollectionCount(2)) { DisplayName = "Gen 2 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) };
+ _threadPoolThreadCounter ??= new PollingCounter("threadpool-thread-count", this, () => ThreadPool.ThreadCount) { DisplayName = "ThreadPool Thread Count" };
+ _monitorContentionCounter ??= new IncrementingPollingCounter("monitor-lock-contention-count", this, () => Monitor.LockContentionCount) { DisplayName = "Monitor Lock Contention Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
+ _threadPoolQueueCounter ??= new PollingCounter("threadpool-queue-length", this, () => ThreadPool.PendingWorkItemCount) { DisplayName = "ThreadPool Queue Length" };
+ _completedItemsCounter ??= new IncrementingPollingCounter("threadpool-completed-items-count", this, () => ThreadPool.CompletedWorkItemCount) { DisplayName = "ThreadPool Completed Work Item Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
+ _allocRateCounter ??= new IncrementingPollingCounter("alloc-rate", this, () => GC.GetTotalAllocatedBytes()) { DisplayName = "Allocation Rate", DisplayUnits = "B", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
+ _timerCounter ??= new PollingCounter("active-timer-count", this, () => Timer.ActiveCount) { DisplayName = "Number of Active Timers" };
+
+#if !MONO
+ _exceptionCounter ??= new IncrementingPollingCounter("exception-count", this, () => Exception.GetExceptionCount()) { DisplayName = "Exception Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
+ _gcTimeCounter ??= new PollingCounter("time-in-gc", this, () => GC.GetLastGCPercentTimeInGC()) { DisplayName = "% Time in GC since last GC", DisplayUnits = "%" };
+ _gen0SizeCounter ??= new PollingCounter("gen-0-size", this, () => GC.GetGenerationSize(0)) { DisplayName = "Gen 0 Size", DisplayUnits = "B" };
+ _gen1SizeCounter ??= new PollingCounter("gen-1-size", this, () => GC.GetGenerationSize(1)) { DisplayName = "Gen 1 Size", DisplayUnits = "B" };
+ _gen2SizeCounter ??= new PollingCounter("gen-2-size", this, () => GC.GetGenerationSize(2)) { DisplayName = "Gen 2 Size", DisplayUnits = "B" };
+ _lohSizeCounter ??= new PollingCounter("loh-size", this, () => GC.GetGenerationSize(3)) { DisplayName = "LOH Size", DisplayUnits = "B" };
+ _assemblyCounter ??= new PollingCounter("assembly-count", this, () => System.Reflection.Assembly.GetAssemblyCount()) { DisplayName = "Number of Assemblies Loaded" };
+#endif
+ }
+
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/RuntimeEventSourceHelper.Unix.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/RuntimeEventSourceHelper.Unix.cs
new file mode 100644
index 000000000..7b6e43f9f
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/RuntimeEventSourceHelper.Unix.cs
@@ -0,0 +1,14 @@
+// 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.
+
+namespace System.Diagnostics.Tracing
+{
+ internal static class RuntimeEventSourceHelper
+ {
+ private static Interop.Sys.ProcessCpuInformation s_cpuInfo;
+
+ internal static int GetCpuUsage() =>
+ Interop.Sys.GetCpuUtilization(ref s_cpuInfo) / Environment.ProcessorCount;
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/RuntimeEventSourceHelper.Windows.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/RuntimeEventSourceHelper.Windows.cs
new file mode 100644
index 000000000..ad198646d
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/RuntimeEventSourceHelper.Windows.cs
@@ -0,0 +1,49 @@
+// 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.
+
+namespace System.Diagnostics.Tracing
+{
+ internal static class RuntimeEventSourceHelper
+ {
+ private static long s_prevProcUserTime = 0;
+ private static long s_prevProcKernelTime = 0;
+ private static long s_prevSystemUserTime = 0;
+ private static long s_prevSystemKernelTime = 0;
+
+ internal static int GetCpuUsage()
+ {
+ // Returns the current process' CPU usage as a percentage
+
+ int cpuUsage;
+
+ if (!Interop.Kernel32.GetProcessTimes(Interop.Kernel32.GetCurrentProcess(), out _, out _, out long procKernelTime, out long procUserTime))
+ {
+ return 0;
+ }
+
+ if (!Interop.Kernel32.GetSystemTimes(out _, out long systemUserTime, out long systemKernelTime))
+ {
+ return 0;
+ }
+
+ if (s_prevSystemUserTime == 0 && s_prevSystemKernelTime == 0) // These may be 0 when we report CPU usage for the first time, in which case we should just return 0.
+ {
+ cpuUsage = 0;
+ }
+ else
+ {
+ long totalProcTime = (procUserTime - s_prevProcUserTime) + (procKernelTime - s_prevProcKernelTime);
+ long totalSystemTime = (systemUserTime - s_prevSystemUserTime) + (systemKernelTime - s_prevSystemKernelTime);
+ cpuUsage = (int)(totalProcTime * 100 / totalSystemTime);
+ }
+
+ s_prevProcUserTime = procUserTime;
+ s_prevProcKernelTime = procKernelTime;
+ s_prevSystemUserTime = systemUserTime;
+ s_prevSystemKernelTime = systemKernelTime;
+
+ return cpuUsage;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventHandleTable.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventHandleTable.cs
new file mode 100644
index 000000000..9c294cf8e
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventHandleTable.cs
@@ -0,0 +1,61 @@
+// 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;
+
+namespace System.Diagnostics.Tracing
+{
+#if FEATURE_PERFTRACING
+ ///
+ /// Per-EventSource data structure for caching EventPipe EventHandles associated with TraceLogging events.
+ ///
+ internal sealed class TraceLoggingEventHandleTable
+ {
+ private const int DefaultLength = 10;
+ private IntPtr[] m_innerTable;
+
+ internal TraceLoggingEventHandleTable()
+ {
+ m_innerTable = new IntPtr[DefaultLength];
+ }
+
+ internal IntPtr this[int eventID]
+ {
+ get
+ {
+ IntPtr ret = IntPtr.Zero;
+ IntPtr[] innerTable = Volatile.Read(ref m_innerTable);
+
+ if (eventID >= 0 && eventID < innerTable.Length)
+ {
+ ret = innerTable[eventID];
+ }
+
+ return ret;
+ }
+ }
+
+ internal void SetEventHandle(int eventID, IntPtr eventHandle)
+ {
+ // Set operations must be serialized to ensure that re-size operations don't lose concurrent writes.
+ Debug.Assert(Monitor.IsEntered(this));
+
+ if (eventID >= m_innerTable.Length)
+ {
+ int newSize = m_innerTable.Length * 2;
+ if (newSize <= eventID)
+ {
+ newSize = eventID + 1;
+ }
+
+ IntPtr[] newTable = new IntPtr[newSize];
+ Array.Copy(m_innerTable, newTable, m_innerTable.Length);
+ Volatile.Write(ref m_innerTable, newTable);
+ }
+
+ m_innerTable[eventID] = eventHandle;
+ }
+ }
+#endif
+}
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
index 736e78d3a..aa13daa55 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
@@ -523,8 +523,7 @@ namespace System.Diagnostics.Tracing
fixed (EventSourceOptions* pOptions = &options)
{
- EventDescriptor descriptor;
- NameInfo? nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out descriptor);
+ NameInfo? nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out EventDescriptor descriptor);
if (nameInfo == null)
{
return;
@@ -591,9 +590,8 @@ namespace System.Diagnostics.Tracing
{
fixed (EventSourceOptions* pOptions = &options)
{
- EventDescriptor descriptor;
options.Opcode = options.IsOpcodeSet ? options.Opcode : GetOpcodeWithDefault(options.Opcode, eventName);
- NameInfo? nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out descriptor);
+ NameInfo? nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out EventDescriptor descriptor);
if (nameInfo == null)
{
return;
@@ -764,8 +762,7 @@ namespace System.Diagnostics.Tracing
if (m_traits[i].StartsWith("ETW_", StringComparison.Ordinal))
{
string etwTrait = m_traits[i].Substring(4);
- byte traitNum;
- if (!byte.TryParse(etwTrait, out traitNum))
+ if (!byte.TryParse(etwTrait, out byte traitNum))
{
if (etwTrait == "GROUP")
{
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs
index a5de12342..b0b537e76 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs
@@ -164,8 +164,7 @@ namespace System.Diagnostics.Tracing
{
Dictionary cache = threadCache ??= new Dictionary();
- TraceLoggingTypeInfo? instance;
- if (!cache.TryGetValue(type, out instance))
+ if (!cache.TryGetValue(type, out TraceLoggingTypeInfo? instance))
{
recursionCheck ??= new List();
int recursionCheckCount = recursionCheck.Count;
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.cs
new file mode 100644
index 000000000..f027781f4
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.cs
@@ -0,0 +1,204 @@
+// 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 System.Collections.ObjectModel;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+
+#if FEATURE_EVENTSOURCE_XPLAT
+
+namespace System.Diagnostics.Tracing
+{
+ internal class XplatEventLogger : EventListener
+ {
+ private static Lazy eventSourceNameFilter = new Lazy(() => CompatibilitySwitch.GetValueInternal("EventSourceFilter"));
+ private static Lazy eventSourceEventFilter = new Lazy(() => CompatibilitySwitch.GetValueInternal("EventNameFilter"));
+
+ public XplatEventLogger() {}
+
+ private static bool initializedPersistentListener = false;
+
+ public static EventListener? InitializePersistentListener()
+ {
+ try
+ {
+ if (!initializedPersistentListener && XplatEventLogger.IsEventSourceLoggingEnabled())
+ {
+ initializedPersistentListener = true;
+ return new XplatEventLogger();
+ }
+ }
+ catch (Exception) { }
+
+ return null;
+ }
+
+ [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
+ private static extern bool IsEventSourceLoggingEnabled();
+
+ [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
+ private static extern void LogEventSource(int eventID, string? eventName, string eventSourceName, string payload);
+
+ private static readonly List escape_seq = new List { '\b', '\f', '\n', '\r', '\t', '\"', '\\' };
+ private static readonly Dictionary seq_mapping = new Dictionary()
+ {
+ {'\b', "b"},
+ {'\f', "f"},
+ {'\n', "n"},
+ {'\r', "r"},
+ {'\t', "t"},
+ {'\"', "\\\""},
+ {'\\', "\\\\"}
+ };
+
+ private static void minimalJsonserializer(string payload, StringBuilder sb)
+ {
+ foreach (var elem in payload)
+ {
+ if (escape_seq.Contains(elem))
+ {
+ sb.Append("\\\\");
+ sb.Append(seq_mapping[elem]);
+ }
+ else
+ {
+ sb.Append(elem);
+ }
+ }
+ }
+
+ private static string Serialize(ReadOnlyCollection? payloadName, ReadOnlyCollection