diff --git a/src/PathBench/MethodProfileReportFormatter.Graphviz.cs b/src/PathBench/MethodProfileReportFormatter.Graphviz.cs index 1962dcc..3d72fb3 100644 --- a/src/PathBench/MethodProfileReportFormatter.Graphviz.cs +++ b/src/PathBench/MethodProfileReportFormatter.Graphviz.cs @@ -86,17 +86,17 @@ public static string SanitizeIdentifier(string name) { var sb = new StringBuilder(); sb.Append("__"); - foreach (var c in name.EnumerateRunes()) + foreach (var c in name) { - switch (c.Value) + switch (c) { - case >= 0x30 and < 0x3A: - case >= 0x41 and < 0x5B: - case >= 0x61 and < 0x7B: + case >= (char)0x30 and < (char)0x3A: + case >= (char)0x41 and < (char)0x5B: + case >= (char)0x61 and < (char)0x7B: sb.Append(c); continue; default: - sb.Append($"_u{(uint)c.Value:X04}_"); + sb.Append($"_u{(uint)c:X04}_"); continue; } } @@ -106,9 +106,9 @@ public static string SanitizeIdentifier(string name) public static string SanitizeLabel(string name) { var sb = new StringBuilder(name.Length * 2); - foreach (var c in name.EnumerateRunes()) + foreach (var c in name) { - switch (c.Value) + switch (c) { case '\n': sb.Append(@"\n"); diff --git a/src/PathBench/PathBench.csproj b/src/PathBench/PathBench.csproj index 004c05a..ada4ef4 100644 --- a/src/PathBench/PathBench.csproj +++ b/src/PathBench/PathBench.csproj @@ -1,7 +1,8 @@  - net10.0 + + net10.0;netstandard2.0 True @@ -25,4 +26,12 @@ + + + + + + + + diff --git a/src/PathBench/PreciseDuration.cs b/src/PathBench/PreciseDuration.cs index 135be56..c344e32 100644 --- a/src/PathBench/PreciseDuration.cs +++ b/src/PathBench/PreciseDuration.cs @@ -1,4 +1,6 @@ +#if NET7_0_OR_GREATER using System.Numerics; +#endif using System.Text.RegularExpressions; namespace PathBench; @@ -9,9 +11,11 @@ namespace PathBench; public readonly partial struct PreciseDuration(long ticks) : IEquatable , IComparable +#if NET7_0_OR_GREATER , IComparisonOperators , IAdditionOperators , ISubtractionOperators +#endif , IFormattable { /// @@ -202,8 +206,14 @@ public static PreciseDuration FromNanoseconds(double nanoseconds) => #region ToString overloads - [GeneratedRegex(@"^(?.+?)(?nsec|usec|msec|sec|)$")] + //lang=regex + private const string _formatMatchingPattern = @"^(?.+?)(?nsec|usec|msec|sec|)$"; +#if NET7_0_OR_GREATER + [GeneratedRegex(_formatMatchingPattern)] private static partial Regex GetFormatMatcher(); +#else + private static Regex GetFormatMatcher() => new (_formatMatchingPattern, RegexOptions.Compiled); +#endif private static readonly Regex _FormatMatcher = GetFormatMatcher(); /// @@ -279,7 +289,7 @@ public string ToString(string? realPartFormat, TimeScale timeScale, IFormatProvi _ => throw new FormatException("Invalid time scale."), }; - #endregion ToString overloads +#endregion ToString overloads /// [ExcludeFromCodeCoverage] diff --git a/src/PathBench/__FrameworkCompatibility.cs b/src/PathBench/__FrameworkCompatibility.cs new file mode 100644 index 0000000..5283430 --- /dev/null +++ b/src/PathBench/__FrameworkCompatibility.cs @@ -0,0 +1,111 @@ +#pragma warning disable IDE0079 // Remove unnecessary suppression +#pragma warning disable IDE0130 // Namespace does not match folder structure + +#if !NET5_0_OR_GREATER +using System.Runtime.CompilerServices; + +namespace System.Runtime.CompilerServices +{ + internal static class IsExternalInit; +} + +namespace System.Runtime.InteropServices +{ + internal static class CollectionsMarshal + { + private sealed class ListCompat + { + internal T[]? _items = default; // Do not rename (binary serialization) + internal int _size = default; // Do not rename (binary serialization) + internal int _version = default; // Do not rename (binary serialization) + } + + public static ReadOnlySpan AsSpan(List? list) + { + Span span = default; + if (list is not null) + { + var listCompat = Unsafe.As, ListCompat>(ref list); + int size = listCompat._size; + T[] items = listCompat._items!; + + if ((uint)size > (uint)items.Length) + { + throw new InvalidOperationException(); + } + + span = items.AsSpan(0, size); + } + + return span; + } + } +} +#endif + +#if !NET7_0_OR_GREATER +namespace System +{ + internal static class TimeSpanExtensions + { + extension(TimeSpan ts) + { + public static long TicksPerMicrosecond => TimeSpan.TicksPerMillisecond / 1000; + } + } +} +#endif + +#if !NET9_0_OR_GREATER +namespace System.Threading +{ + internal class Lock + { + private sealed class Scope(object token) : IDisposable + { + public void Dispose() + { + Monitor.Exit(token); + } + } + + private readonly object _token = new(); + + internal IDisposable EnterScope() + { + Monitor.Enter(_token); + return new Scope(_token); + } + } +} +#endif + +#if !NETSTANDARD2_1_OR_GREATER && !NETCOREAPP2_0_OR_GREATER +namespace System.Collections.Generic +{ + internal static class CollectionExtensions + { + extension(IDictionary dict) + { + public bool TryAdd(TKey key, TValue value) + { + if (dict.ContainsKey(key)) return false; + dict.Add(key, value); + return true; + } + } + + extension(KeyValuePair kv) + { + public void Deconstruct(out TKey key, out TValue value) + { + key = kv.Key; + value = kv.Value; + } + } + } +} +#endif + +#pragma warning restore IDE0130 // Namespace does not match folder structure +#pragma warning restore IDE0079 // Remove unnecessary suppression \ No newline at end of file diff --git a/tests/PathBench.Test/MethodProfileReportFormatter.GraphvizTest.cs b/tests/PathBench.Test/MethodProfileReportFormatter.GraphvizTest.cs index 3a461ff..dabba76 100644 --- a/tests/PathBench.Test/MethodProfileReportFormatter.GraphvizTest.cs +++ b/tests/PathBench.Test/MethodProfileReportFormatter.GraphvizTest.cs @@ -1,3 +1,4 @@ +using System.Reflection; using System.Runtime.CompilerServices; namespace PathBench.Test; @@ -9,6 +10,8 @@ private static class PrivateAccess public const string _typeName = $"{nameof(PathBench)}.{nameof(MethodProfileReportFormatter)}+GraphvizStyle_, PathBench"; +#if NET10_0_OR_GREATER + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "SanitizeIdentifier")] public extern static string SanitizeIdentifier( [UnsafeAccessorType(_typeName)] object? _, @@ -18,6 +21,29 @@ public extern static string SanitizeIdentifier( public extern static string SanitizeLabel( [UnsafeAccessorType(_typeName)] object? _, string input); + +#else + private static readonly Type _GraphvizStyle_ = + typeof(MethodProfileReportFormatter) + .GetNestedType("GraphvizStyle_", BindingFlags.NonPublic)!; + + private static readonly MethodInfo _SanitizeIdentifier = + _GraphvizStyle_.GetMethod("SanitizeIdentifier", BindingFlags.Public | BindingFlags.Static)!; + + private static readonly MethodInfo _SanitizeLabel = + _GraphvizStyle_.GetMethod("SanitizeLabel", BindingFlags.Public | BindingFlags.Static)!; + + public static string SanitizeIdentifier(object? _, string input) + { + return (string)_SanitizeIdentifier.Invoke(null, [input])!; + } + + public static string SanitizeLabel(object? _, string input) + { + return (string)_SanitizeLabel.Invoke(null, [input])!; + } + +#endif } public static TheoryData SanitizeIdentifierTestCases() => diff --git a/tests/PathBench.Test/PathBench.Test.csproj b/tests/PathBench.Test/PathBench.Test.csproj index 4abab7a..e3530fb 100644 --- a/tests/PathBench.Test/PathBench.Test.csproj +++ b/tests/PathBench.Test/PathBench.Test.csproj @@ -1,7 +1,7 @@  - net10.0 + net8.0;net10.0 enable enable false