diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 0000000..a38b718
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,65 @@
+# Copilot Instructions for PathBench
+
+## What's this file for?
+
+- This file provides instructions for generating source or operating as an agent
+ for this repository to GitHub Copilot or other AI code assistants.
+- When implementing new features, please use the technology selection,
+ design policy, and module configuration shown here as a premise.
+- If in doubt, explore the files in the repository and ask the user "Is this
+ what you meant?"
+
+## Preconditions
+
+- The comments or documentations in the source codes must be in English unless
+ there is specific instructions.
+- The answer in the shell writes in user local language as possible as.
+- When making code changes, if the number of lines modified is likely to exceed
+ 200, please prompt the user beforehand with the message: "This instruction may
+ result in over 200 lines of code being modified. Do you wish to proceed?"
+- When making significant changes, first create a plan outlining what you intend
+ to do, then propose it to the user by saying, "We plan to proceed with this
+ approach." If the user requests modifications to the plan at this stage,
+ revise the plan accordingly and resubmit it.
+
+## Project overview
+
+PathBench is a .NET library for benchmarking and analyzing the performance of
+different algorithms and data structures. It provides a way to measure execution
+time, allowing developers to optimize their code effectively.
+
+Please refer to the [README.md](../README.md) for more details about the
+project, including its features, installation instructions, and usage examples.
+
+## Directories and files
+
+This project has the following directory structure:
+
+```
+./
++-- .editorconfig # Editor configuration file
++-- .gitignore # Git ignore file
++-- Directory.Build.props # MSBuild shared properties for the project
++-- LICENSE.txt # License information for the project
++-- Measure-Coverage.ps1 # PowerShell script for measuring code coverage
++-- PathBench.slnx # Visual Studio solution file
++-- README.md # Project overview and documentation
++-- global.json # .NET SDK version configuration
++-- .github/
+| +-- workflows/ # GitHub Actions workflow files
+| | +-- dotnet-CI.yml # CI workflow for .NET builds and tests
+| +-- copilot-instructions.md # This file: instructions for GitHub Copilot
++-- resources/ # Misc. resource files for the project
++-- src/ # Source code for the library
+| +-- PathBench/ # Main library code
++ tests/ # Unit tests for the library
+ +-- PathBench.Test/ # Unit tests for the library
+ +-- etc/
+ +-- coverlet.runsettings # Coverlet configuration for code coverage analysis
+```
+
+## Coding style
+
+- Unless specific commented, the coding style should follow .editorconfig file.
+- Unless specific commented, the coding style should follow the [Microsoft C# coding conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions).
+- All of public members must have XML documentation comments.
diff --git a/README.md b/README.md
index c18cac7..093521f 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,10 @@
Code path performance monitoring tool.
+[
](https://github.com/aka-nse/PathBench/)
+
+[
](https://www.nuget.org/packages/akanse.PathBench/)
+
----
## Features
@@ -95,3 +99,7 @@ static class SampleClass
### v1.0.0
- First release
+
+### v1.1.0
+
+- Adds support for net standard 2.0
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 d33b87c..d2eecea 100644
--- a/src/PathBench/PathBench.csproj
+++ b/src/PathBench/PathBench.csproj
@@ -1,7 +1,8 @@
- net10.0
+
+ net10.0;netstandard2.0
True
@@ -9,7 +10,7 @@
akanse.$(AssemblyName)
PathBench
- 1.0.0
+ 1.1.0
PathBench
Code path performance monitoring tool.
@@ -21,7 +22,16 @@
+
+
+
+
+
+
+
+
+
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