diff --git a/.idea/.idea.Benchmark/.idea/.gitignore b/.idea/.idea.Benchmark/.idea/.gitignore
new file mode 100644
index 0000000..d7696ea
--- /dev/null
+++ b/.idea/.idea.Benchmark/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/.idea.Benchmark.iml
+/projectSettingsUpdater.xml
+/modules.xml
+/contentModel.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/.idea.Benchmark/.idea/encodings.xml b/.idea/.idea.Benchmark/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.Benchmark/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Benchmark/.idea/indexLayout.xml b/.idea/.idea.Benchmark/.idea/indexLayout.xml
new file mode 100644
index 0000000..27ba142
--- /dev/null
+++ b/.idea/.idea.Benchmark/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Benchmark/.idea/misc.xml b/.idea/.idea.Benchmark/.idea/misc.xml
new file mode 100644
index 0000000..28a804d
--- /dev/null
+++ b/.idea/.idea.Benchmark/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Benchmark/riderModule.iml b/.idea/.idea.Benchmark/riderModule.iml
new file mode 100644
index 0000000..2cac41e
--- /dev/null
+++ b/.idea/.idea.Benchmark/riderModule.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..0a08bc7
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,27 @@
+{
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/Benchmark.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "internalConsole",
+ "stopAtEntry": false
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "${command:pickProcess}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..3c8a7cc
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,42 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/Benchmark.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/Benchmark.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "watch",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "watch",
+ "run",
+ "${workspaceFolder}/Benchmark.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Benchmark.csproj b/Benchmark.csproj
new file mode 100644
index 0000000..b1a51fc
--- /dev/null
+++ b/Benchmark.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Benchmark.sln b/Benchmark.sln
new file mode 100644
index 0000000..464144b
--- /dev/null
+++ b/Benchmark.sln
@@ -0,0 +1,48 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "Benchmark.csproj", "{994B1E75-4E3A-4A11-8363-FFB5FC759A5B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject1", "TestProject1\TestProject1.csproj", "{C6BD6A51-7394-4CDD-962C-2C39AC2D846C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Debug|x64.Build.0 = Debug|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Debug|x86.Build.0 = Debug|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Release|x64.ActiveCfg = Release|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Release|x64.Build.0 = Release|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Release|x86.ActiveCfg = Release|Any CPU
+ {994B1E75-4E3A-4A11-8363-FFB5FC759A5B}.Release|x86.Build.0 = Release|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Debug|x64.Build.0 = Debug|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Debug|x86.Build.0 = Debug|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Release|x64.ActiveCfg = Release|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Release|x64.Build.0 = Release|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Release|x86.ActiveCfg = Release|Any CPU
+ {C6BD6A51-7394-4CDD-962C-2C39AC2D846C}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/Identity.cs b/Identity.cs
new file mode 100644
index 0000000..ea9deb4
--- /dev/null
+++ b/Identity.cs
@@ -0,0 +1,67 @@
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Benchmark
+{
+ public readonly struct Identity
+ {
+ public Identity(string prefix, int index, int revision)
+ {
+ Prefix = prefix;
+ Index = index;
+ Revision = revision;
+ }
+
+ public string Prefix { get; }
+ public int Index { get; }
+ public int Revision { get; }
+
+ public static bool TryParse(string value, out Identity identity)
+ {
+ identity = default;
+ var position = value.Length;
+ if (!TryGetNumber(value, null, ref position, out var revision)) return false;
+ if (value[position] != '-') return false;
+ if (!TryGetNumber(value, 7, ref position, out var index)) return false;
+ if (!TryGetPrefix(value, ref position, out var prefix)) return false;
+ identity = new Identity(prefix, index, revision);
+ return true;
+ }
+
+ private static bool TryGetPrefix(in string identity, ref int lastPosition, out string prefix)
+ {
+ prefix = identity.Remove(lastPosition + 1);
+ if (prefix.Length <= 3) return false;
+ return !prefix.Any(character => character < 'A' || character > 'Z');
+ }
+
+ private static bool TryGetNumber(in string identity, int? exactLength, ref int currentPosition, out int revision)
+ {
+ revision = 0;
+ var start = currentPosition;
+ for (currentPosition = start - 1; currentPosition >= 0; currentPosition--)
+ {
+ var current = identity[currentPosition];
+ if(current == '0') continue;
+ if (current < '1' || current > '9') break;
+ revision += (current - '0') * IntegerPow(10, start - currentPosition - 1);
+ }
+
+ if (exactLength != null && start - (currentPosition + 1) != exactLength) return false;
+ return revision != default;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int IntegerPow(int x, int y)
+ {
+ if (y == 0) return 1;
+ var result = x;
+ for (var i = 1; i < y; i++)
+ {
+ result *= x;
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/IdentityParser.cs b/IdentityParser.cs
new file mode 100644
index 0000000..e6ea687
--- /dev/null
+++ b/IdentityParser.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Text.RegularExpressions;
+using BenchmarkDotNet.Attributes;
+
+namespace Benchmark
+{
+ [MemoryDiagnoser]
+ public class IdentityParser
+ {
+ private const string TestIdentity = "ABZ0000001-1";
+ private readonly Regex _regex = new Regex(@"^(\w{3,})(\d{7})-(\d+)$", RegexOptions.Compiled);
+
+ [Benchmark]
+ public Identity? Parse1()
+ {
+ var match = _regex.Match(TestIdentity);
+ if (!match.Success) return default;
+ var prefix = match.Groups[1].Value;
+ var index = int.Parse(match.Groups[2].Value);
+ var revision = int.Parse(match.Groups[2].Value);
+ return new Identity(prefix, index, revision);
+ }
+
+ [Benchmark]
+ public Identity? Parse2()
+ {
+ return Identity.TryParse(TestIdentity, out var identity) ? identity : default(Identity?);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
new file mode 100644
index 0000000..9c7b0fe
--- /dev/null
+++ b/Program.cs
@@ -0,0 +1,13 @@
+using BenchmarkDotNet.Running;
+
+namespace Benchmark
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ //var summary = BenchmarkRunner.Run();
+ var summary = BenchmarkRunner.Run();
+ }
+ }
+}
diff --git a/TestProject1/TestProject1.csproj b/TestProject1/TestProject1.csproj
new file mode 100644
index 0000000..9c5a28e
--- /dev/null
+++ b/TestProject1/TestProject1.csproj
@@ -0,0 +1,19 @@
+
+
+
+ netcoreapp3.1
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TestProject1/UnitTest1.cs b/TestProject1/UnitTest1.cs
new file mode 100644
index 0000000..692ccf1
--- /dev/null
+++ b/TestProject1/UnitTest1.cs
@@ -0,0 +1,82 @@
+using Benchmark;
+using NUnit.Framework;
+
+namespace TestProject1
+{
+ public class Tests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ }
+
+ [Test]
+ public void Test3()
+ {
+ var actual = new TrailingNumberFromString().GetTrailingNumberFromString3();
+ Assert.AreEqual(1234, actual);
+ }
+
+ [Test]
+ public void Test4()
+ {
+ var actual = new TrailingNumberFromString().GetTrailingNumberFromString4();
+ Assert.AreEqual(1234, actual);
+ }
+
+ [Test]
+ public void Test5()
+ {
+ var actual = new TrailingNumberFromString().GetTrailingNumberFromString5();
+ Assert.AreEqual(1234, actual);
+ }
+
+ [Test]
+ public void Test6()
+ {
+ var actual = new IdentityParser().Parse1();
+ Assert.AreEqual("ABZ", actual?.Prefix);
+ Assert.AreEqual(1, actual?.Index);
+ Assert.AreEqual(1, actual?.Revision);
+ }
+
+ [Test]
+ public void Test7()
+ {
+ var actual = new IdentityParser().Parse2();
+ Assert.AreEqual("ABZ", actual?.Prefix);
+ Assert.AreEqual(1, actual?.Index);
+ Assert.AreEqual(1, actual?.Revision);
+ }
+
+ [Test]
+ public void Test8()
+ {
+ Assert.IsTrue(Identity.TryParse("TOPFTP0000123-12", out var actual));
+ Assert.AreEqual("TOPFTP", actual.Prefix);
+ Assert.AreEqual(123, actual.Index);
+ Assert.AreEqual(12, actual.Revision);
+ }
+
+ [Test]
+ public void Test9()
+ {
+ Assert.IsTrue(Identity.TryParse("WPR0001023-12", out var actual));
+ Assert.AreEqual("WPR", actual.Prefix);
+ Assert.AreEqual(1023, actual.Index);
+ Assert.AreEqual(12, actual.Revision);
+ }
+
+ [Test]
+ public void Test10()
+ {
+ Assert.IsFalse(Identity.TryParse("WPR0001023+12", out _));
+ Assert.IsFalse(Identity.TryParse("Peter", out _));
+ Assert.IsFalse(Identity.TryParse("NCL1-1", out _));
+ Assert.IsFalse(Identity.TryParse("NCL0000001-0", out _));
+ Assert.IsFalse(Identity.TryParse("NCL0000000-1", out _));
+ Assert.IsFalse(Identity.TryParse("Nc0000001-1", out _));
+ Assert.IsFalse(Identity.TryParse("0000001-1", out _));
+ }
+ }
+}
\ No newline at end of file
diff --git a/TrailingNumberFromString.cs b/TrailingNumberFromString.cs
new file mode 100644
index 0000000..861d475
--- /dev/null
+++ b/TrailingNumberFromString.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+using BenchmarkDotNet.Attributes;
+
+namespace Benchmark
+{
+ [MemoryDiagnoser]
+ public class TrailingNumberFromString
+ {
+ private readonly Regex _regex = new Regex(@"^\d$", RegexOptions.Compiled);
+
+ [Benchmark]
+ public int? GetTrailingNumberFromString1()
+ {
+ const string foo = "Test1234";
+ string sValue = null;
+
+ for (var i = foo.Length - 1; i >= 0; i--)
+ {
+ var regex = new Regex(@"^\d$");
+
+ if (regex.IsMatch(foo[i].ToString()))
+ sValue = foo[i] + sValue;
+ else
+ break;
+ }
+
+ if (sValue != null)
+ return Convert.ToInt32(sValue);
+
+ return null;
+ }
+
+ [Benchmark]
+ public int? GetTrailingNumberFromString2()
+ {
+ const string foo = "Test1234";
+ string sValue = null;
+
+ for (var i = foo.Length - 1; i >= 0; i--)
+ {
+ if (_regex.IsMatch(foo[i].ToString()))
+ sValue = foo[i] + sValue;
+ else
+ break;
+ }
+ if (sValue != null)
+ return Convert.ToInt32(sValue);
+
+ return null;
+ }
+
+ [Benchmark]
+ public int? GetTrailingNumberFromString3()
+ {
+ const string foo = "Test1234";
+ var result = 0;
+
+ for (var i = foo.Length - 1; i >= 0; i--)
+ {
+ if (foo[i] >= 48 && foo[i] <= 58)
+ {
+ var value = foo[i] - 48;
+ result += value * (int) Math.Pow(10, foo.Length - i - 1);
+ continue;
+ }
+
+ break;
+ }
+
+ return result;
+ }
+
+ [Benchmark]
+ public int? GetTrailingNumberFromString4()
+ {
+ const string foo = "Test1234";
+ var result = 0;
+
+ for (var i = foo.Length - 1; i >= 0; i--)
+ {
+ if (foo[i] >= 48 && foo[i] <= 58)
+ {
+ var value = foo[i] - 48;
+ result += value * IntegerPow(10, foo.Length - i - 1);
+ continue;
+ }
+
+ break;
+ }
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int IntegerPow(int x, int y)
+ {
+ if (y == 0) return 1;
+ var result = x;
+ for (var i = 1; i < y; i++)
+ {
+ result *= x;
+ }
+
+ return result;
+ }
+
+ [Benchmark]
+ public int? GetTrailingNumberFromString5()
+ {
+ const string foo = "Test1234";
+ var regex = new Regex(@"\d+$");
+ var match = regex.Match(foo);
+ return match.Success && int.TryParse(match.Value, out var result) ? result : default(int?);
+ }
+ }
+}
\ No newline at end of file