Skip to content

Commit 240783a

Browse files
committed
refactor(parser): change user agent parsing to use ReadOnlySpan<char>
1 parent d767fb4 commit 240783a

File tree

3 files changed

+29
-30
lines changed

3 files changed

+29
-30
lines changed

perf/HttpUserAgentParser.Benchmarks/HttpUserAgentParserBenchmarks.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
using BenchmarkDotNet.Attributes;
44
using BenchmarkDotNet.Jobs;
5+
using MyCSharp.HttpUserAgentParser;
56

67
#if OS_WIN
78
using BenchmarkDotNet.Diagnostics.Windows.Configs;
89
#endif
910

10-
namespace MyCSharp.HttpUserAgentParser.Benchmarks;
11+
namespace HttpUserAgentParser.Benchmarks;
1112

1213
[MemoryDiagnoser]
1314
[SimpleJob(RuntimeMoniker.Net80)]
@@ -43,7 +44,7 @@ public void Parse()
4344

4445
for (int i = 0; i < testUserAgentMix.Length; ++i)
4546
{
46-
results[i] = HttpUserAgentParser.Parse(testUserAgentMix[i]);
47+
results[i] = MyCSharp.HttpUserAgentParser.HttpUserAgentParser.Parse(testUserAgentMix[i]);
4748
}
4849
}
4950
}

perf/HttpUserAgentParser.Benchmarks/LibraryComparison/LibraryComparisonBenchmarks.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
using BenchmarkDotNet.Configs;
66
using BenchmarkDotNet.Diagnosers;
77
using DeviceDetectorNET;
8+
using MyCSharp.HttpUserAgentParser;
89
using MyCSharp.HttpUserAgentParser.Providers;
910

10-
namespace MyCSharp.HttpUserAgentParser.Benchmarks.LibraryComparison;
11+
namespace HttpUserAgentParser.Benchmarks.LibraryComparison;
1112

1213
[ShortRunJob]
1314
[MemoryDiagnoser]
@@ -33,7 +34,7 @@ public IEnumerable<TestData> GetTestUserAgents()
3334
[BenchmarkCategory("Basic")]
3435
public HttpUserAgentInformation MyCSharpBasic()
3536
{
36-
HttpUserAgentInformation info = HttpUserAgentParser.Parse(Data.UserAgent);
37+
HttpUserAgentInformation info = MyCSharp.HttpUserAgentParser.HttpUserAgentParser.Parse(Data.UserAgent);
3738
return info;
3839
}
3940

src/HttpUserAgentParser/HttpUserAgentParser.cs

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,48 +16,46 @@ public static class HttpUserAgentParser
1616
/// <summary>
1717
/// Parses given <param name="userAgent">user agent</param>
1818
/// </summary>
19-
public static HttpUserAgentInformation Parse(string userAgent)
19+
public static HttpUserAgentInformation Parse(ReadOnlySpan<char> userAgent)
2020
{
2121
// prepare
2222
userAgent = Cleanup(userAgent);
2323

2424
// analyze
2525
if (TryGetRobot(userAgent, out string? robotName))
2626
{
27-
return HttpUserAgentInformation.CreateForRobot(userAgent, robotName);
27+
return HttpUserAgentInformation.CreateForRobot(userAgent.ToString(), robotName);
2828
}
2929

3030
HttpUserAgentPlatformInformation? platform = GetPlatform(userAgent);
3131
string? mobileDeviceType = GetMobileDevice(userAgent);
3232

3333
if (TryGetBrowser(userAgent, out (string Name, string? Version)? browser))
3434
{
35-
return HttpUserAgentInformation.CreateForBrowser(userAgent, platform, browser?.Name, browser?.Version, mobileDeviceType);
35+
return HttpUserAgentInformation.CreateForBrowser(userAgent.ToString(), platform, browser?.Name, browser?.Version, mobileDeviceType);
3636
}
3737

38-
return HttpUserAgentInformation.CreateForUnknown(userAgent, platform, mobileDeviceType);
38+
return HttpUserAgentInformation.CreateForUnknown(userAgent.ToString(), platform, mobileDeviceType);
3939
}
4040

4141
/// <summary>
4242
/// pre-cleanup of <param name="userAgent">user agent</param>
4343
/// </summary>
44-
public static string Cleanup(string userAgent) => userAgent.Trim();
44+
public static ReadOnlySpan<char> Cleanup(ReadOnlySpan<char> userAgent) => userAgent.Trim();
4545

4646
/// <summary>
4747
/// returns the platform or null
4848
/// </summary>
49-
public static HttpUserAgentPlatformInformation? GetPlatform(string userAgent)
49+
public static HttpUserAgentPlatformInformation? GetPlatform(ReadOnlySpan<char> userAgent)
5050
{
51-
// Fast, allocation-free token scan (keeps public statics untouched)
52-
ReadOnlySpan<char> ua = userAgent.AsSpan();
53-
foreach ((string Token, string Name, HttpUserAgentPlatformType PlatformType) p in HttpUserAgentStatics.s_platformRules)
51+
foreach ((string Token, string Name, HttpUserAgentPlatformType PlatformType) p in HttpUserAgentStatics.s_platformRules)
5452
{
55-
if (ContainsIgnoreCase(ua, p.Token))
53+
if (ContainsIgnoreCase(userAgent, p.Token))
5654
{
5755
return new HttpUserAgentPlatformInformation(
58-
regex: HttpUserAgentStatics.GetPlatformRegexForToken(p.Token),
59-
name: p.Name,
60-
platformType: p.PlatformType);
56+
regex: HttpUserAgentStatics.GetPlatformRegexForToken(p.Token),
57+
name: p.Name,
58+
platformType: p.PlatformType);
6159
}
6260
}
6361

@@ -67,7 +65,7 @@ public static HttpUserAgentInformation Parse(string userAgent)
6765
/// <summary>
6866
/// returns true if platform was found
6967
/// </summary>
70-
public static bool TryGetPlatform(string userAgent, [NotNullWhen(true)] out HttpUserAgentPlatformInformation? platform)
68+
public static bool TryGetPlatform(ReadOnlySpan<char> userAgent, [NotNullWhen(true)] out HttpUserAgentPlatformInformation? platform)
7169
{
7270
platform = GetPlatform(userAgent);
7371
return platform is not null;
@@ -76,12 +74,11 @@ public static bool TryGetPlatform(string userAgent, [NotNullWhen(true)] out Http
7674
/// <summary>
7775
/// returns the browser or null
7876
/// </summary>
79-
public static (string Name, string? Version)? GetBrowser(string userAgent)
77+
public static (string Name, string? Version)? GetBrowser(ReadOnlySpan<char> userAgent)
8078
{
81-
ReadOnlySpan<char> ua = userAgent.AsSpan();
82-
foreach ((string Name, string DetectToken, string? VersionToken) rule in HttpUserAgentStatics.s_browserRules)
79+
foreach ((string Name, string DetectToken, string? VersionToken) rule in HttpUserAgentStatics.s_browserRules)
8380
{
84-
if (!TryIndexOf(ua, rule.DetectToken, out int detectIndex))
81+
if (!TryIndexOf(userAgent, rule.DetectToken, out int detectIndex))
8582
{
8683
continue;
8784
}
@@ -90,7 +87,7 @@ public static (string Name, string? Version)? GetBrowser(string userAgent)
9087
int versionSearchStart = detectIndex;
9188
if (!string.IsNullOrEmpty(rule.VersionToken))
9289
{
93-
if (TryIndexOf(ua, rule.VersionToken!, out int vtIndex))
90+
if (TryIndexOf(userAgent, rule.VersionToken!, out int vtIndex))
9491
{
9592
versionSearchStart = vtIndex + rule.VersionToken!.Length;
9693
}
@@ -106,9 +103,9 @@ public static (string Name, string? Version)? GetBrowser(string userAgent)
106103
}
107104

108105
string? version = null;
109-
if (TryExtractVersion(ua, versionSearchStart, out Range range))
106+
if (TryExtractVersion(userAgent, versionSearchStart, out Range range))
110107
{
111-
version = userAgent.AsSpan(range.Start.Value, range.End.Value - range.Start.Value).ToString();
108+
version = userAgent.ToString();
112109
}
113110

114111
return (rule.Name, version);
@@ -120,7 +117,7 @@ public static (string Name, string? Version)? GetBrowser(string userAgent)
120117
/// <summary>
121118
/// returns true if browser was found
122119
/// </summary>
123-
public static bool TryGetBrowser(string userAgent, [NotNullWhen(true)] out (string Name, string? Version)? browser)
120+
public static bool TryGetBrowser(ReadOnlySpan<char> userAgent, [NotNullWhen(true)] out (string Name, string? Version)? browser)
124121
{
125122
browser = GetBrowser(userAgent);
126123
return browser is not null;
@@ -129,7 +126,7 @@ public static bool TryGetBrowser(string userAgent, [NotNullWhen(true)] out (stri
129126
/// <summary>
130127
/// returns the robot or null
131128
/// </summary>
132-
public static string? GetRobot(string userAgent)
129+
public static string? GetRobot(ReadOnlySpan<char> userAgent)
133130
{
134131
foreach ((string key, string value) in HttpUserAgentStatics.Robots)
135132
{
@@ -145,7 +142,7 @@ public static bool TryGetBrowser(string userAgent, [NotNullWhen(true)] out (stri
145142
/// <summary>
146143
/// returns true if robot was found
147144
/// </summary>
148-
public static bool TryGetRobot(string userAgent, [NotNullWhen(true)] out string? robotName)
145+
public static bool TryGetRobot(ReadOnlySpan<char> userAgent, [NotNullWhen(true)] out string? robotName)
149146
{
150147
robotName = GetRobot(userAgent);
151148
return robotName is not null;
@@ -154,7 +151,7 @@ public static bool TryGetRobot(string userAgent, [NotNullWhen(true)] out string?
154151
/// <summary>
155152
/// returns the device or null
156153
/// </summary>
157-
public static string? GetMobileDevice(string userAgent)
154+
public static string? GetMobileDevice(ReadOnlySpan<char> userAgent)
158155
{
159156
foreach ((string key, string value) in HttpUserAgentStatics.Mobiles)
160157
{
@@ -170,7 +167,7 @@ public static bool TryGetRobot(string userAgent, [NotNullWhen(true)] out string?
170167
/// <summary>
171168
/// returns true if device was found
172169
/// </summary>
173-
public static bool TryGetMobileDevice(string userAgent, [NotNullWhen(true)] out string? device)
170+
public static bool TryGetMobileDevice(ReadOnlySpan<char> userAgent, [NotNullWhen(true)] out string? device)
174171
{
175172
device = GetMobileDevice(userAgent);
176173
return device is not null;

0 commit comments

Comments
 (0)