Skip to content

Commit d5ebc22

Browse files
committed
refactor(parser): update user agent methods for string usage
1 parent a23f6a2 commit d5ebc22

File tree

1 file changed

+30
-26
lines changed

1 file changed

+30
-26
lines changed

src/HttpUserAgentParser/HttpUserAgentParser.cs

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,23 @@ public static class HttpUserAgentParser
1414

1515
{
1616
/// <summary>
17-
/// Parses given user agent string without allocating a copy. Prefer this overload to avoid ToString() allocations.
17+
/// Parses given <param name="userAgent">user agent</param>
1818
/// </summary>
1919
public static HttpUserAgentInformation Parse(string userAgent)
2020
{
21-
ReadOnlySpan<char> span = Cleanup(userAgent.AsSpan());
21+
// prepare
22+
userAgent = Cleanup(userAgent);
2223

23-
if (TryGetRobot(span, out string? robotName))
24+
// analyze
25+
if (TryGetRobot(userAgent, out string? robotName))
2426
{
2527
return HttpUserAgentInformation.CreateForRobot(userAgent, robotName);
2628
}
2729

28-
HttpUserAgentPlatformInformation? platform = GetPlatform(span);
29-
string? mobileDeviceType = GetMobileDevice(span);
30+
HttpUserAgentPlatformInformation? platform = GetPlatform(userAgent);
31+
string? mobileDeviceType = GetMobileDevice(userAgent);
3032

31-
if (TryGetBrowser(span, out (string Name, string? Version)? browser))
33+
if (TryGetBrowser(userAgent, out (string Name, string? Version)? browser))
3234
{
3335
return HttpUserAgentInformation.CreateForBrowser(userAgent, platform, browser?.Name, browser?.Version, mobileDeviceType);
3436
}
@@ -39,21 +41,23 @@ public static HttpUserAgentInformation Parse(string userAgent)
3941
/// <summary>
4042
/// pre-cleanup of <param name="userAgent">user agent</param>
4143
/// </summary>
42-
public static ReadOnlySpan<char> Cleanup(ReadOnlySpan<char> userAgent) => userAgent.Trim();
44+
public static string Cleanup(string userAgent) => userAgent.Trim();
4345

4446
/// <summary>
4547
/// returns the platform or null
4648
/// </summary>
47-
public static HttpUserAgentPlatformInformation? GetPlatform(ReadOnlySpan<char> userAgent)
49+
public static HttpUserAgentPlatformInformation? GetPlatform(string userAgent)
4850
{
49-
foreach ((string Token, string Name, HttpUserAgentPlatformType PlatformType) p in HttpUserAgentStatics.s_platformRules)
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)
5054
{
51-
if (ContainsIgnoreCase(userAgent, p.Token))
55+
if (ContainsIgnoreCase(ua, p.Token))
5256
{
5357
return new HttpUserAgentPlatformInformation(
54-
regex: HttpUserAgentStatics.GetPlatformRegexForToken(p.Token),
55-
name: p.Name,
56-
platformType: p.PlatformType);
58+
regex: HttpUserAgentStatics.GetPlatformRegexForToken(p.Token),
59+
name: p.Name,
60+
platformType: p.PlatformType);
5761
}
5862
}
5963

@@ -63,7 +67,7 @@ public static HttpUserAgentInformation Parse(string userAgent)
6367
/// <summary>
6468
/// returns true if platform was found
6569
/// </summary>
66-
public static bool TryGetPlatform(ReadOnlySpan<char> userAgent, [NotNullWhen(true)] out HttpUserAgentPlatformInformation? platform)
70+
public static bool TryGetPlatform(string userAgent, [NotNullWhen(true)] out HttpUserAgentPlatformInformation? platform)
6771
{
6872
platform = GetPlatform(userAgent);
6973
return platform is not null;
@@ -72,11 +76,12 @@ public static bool TryGetPlatform(ReadOnlySpan<char> userAgent, [NotNullWhen(tru
7276
/// <summary>
7377
/// returns the browser or null
7478
/// </summary>
75-
public static (string Name, string? Version)? GetBrowser(ReadOnlySpan<char> userAgent)
79+
public static (string Name, string? Version)? GetBrowser(string userAgent)
7680
{
77-
foreach ((string Name, string DetectToken, string? VersionToken) rule in HttpUserAgentStatics.s_browserRules)
81+
ReadOnlySpan<char> ua = userAgent.AsSpan();
82+
foreach ((string Name, string DetectToken, string? VersionToken) rule in HttpUserAgentStatics.s_browserRules)
7883
{
79-
if (!TryIndexOf(userAgent, rule.DetectToken, out int detectIndex))
84+
if (!TryIndexOf(ua, rule.DetectToken, out int detectIndex))
8085
{
8186
continue;
8287
}
@@ -85,7 +90,7 @@ public static (string Name, string? Version)? GetBrowser(ReadOnlySpan<char> user
8590
int versionSearchStart = detectIndex;
8691
if (!string.IsNullOrEmpty(rule.VersionToken))
8792
{
88-
if (TryIndexOf(userAgent, rule.VersionToken!, out int vtIndex))
93+
if (TryIndexOf(ua, rule.VersionToken!, out int vtIndex))
8994
{
9095
versionSearchStart = vtIndex + rule.VersionToken!.Length;
9196
}
@@ -101,10 +106,9 @@ public static (string Name, string? Version)? GetBrowser(ReadOnlySpan<char> user
101106
}
102107

103108
string? version = null;
104-
if (TryExtractVersion(userAgent, versionSearchStart, out Range range))
109+
if (TryExtractVersion(ua, versionSearchStart, out Range range))
105110
{
106-
// Only allocate the version substring, not the whole user agent
107-
version = userAgent[range].ToString();
111+
version = userAgent.AsSpan(range.Start.Value, range.End.Value - range.Start.Value).ToString();
108112
}
109113

110114
return (rule.Name, version);
@@ -116,7 +120,7 @@ public static (string Name, string? Version)? GetBrowser(ReadOnlySpan<char> user
116120
/// <summary>
117121
/// returns true if browser was found
118122
/// </summary>
119-
public static bool TryGetBrowser(ReadOnlySpan<char> userAgent, [NotNullWhen(true)] out (string Name, string? Version)? browser)
123+
public static bool TryGetBrowser(string userAgent, [NotNullWhen(true)] out (string Name, string? Version)? browser)
120124
{
121125
browser = GetBrowser(userAgent);
122126
return browser is not null;
@@ -125,7 +129,7 @@ public static bool TryGetBrowser(ReadOnlySpan<char> userAgent, [NotNullWhen(true
125129
/// <summary>
126130
/// returns the robot or null
127131
/// </summary>
128-
public static string? GetRobot(ReadOnlySpan<char> userAgent)
132+
public static string? GetRobot(string userAgent)
129133
{
130134
foreach ((string key, string value) in HttpUserAgentStatics.Robots)
131135
{
@@ -141,7 +145,7 @@ public static bool TryGetBrowser(ReadOnlySpan<char> userAgent, [NotNullWhen(true
141145
/// <summary>
142146
/// returns true if robot was found
143147
/// </summary>
144-
public static bool TryGetRobot(ReadOnlySpan<char> userAgent, [NotNullWhen(true)] out string? robotName)
148+
public static bool TryGetRobot(string userAgent, [NotNullWhen(true)] out string? robotName)
145149
{
146150
robotName = GetRobot(userAgent);
147151
return robotName is not null;
@@ -150,7 +154,7 @@ public static bool TryGetRobot(ReadOnlySpan<char> userAgent, [NotNullWhen(true)]
150154
/// <summary>
151155
/// returns the device or null
152156
/// </summary>
153-
public static string? GetMobileDevice(ReadOnlySpan<char> userAgent)
157+
public static string? GetMobileDevice(string userAgent)
154158
{
155159
foreach ((string key, string value) in HttpUserAgentStatics.Mobiles)
156160
{
@@ -166,7 +170,7 @@ public static bool TryGetRobot(ReadOnlySpan<char> userAgent, [NotNullWhen(true)]
166170
/// <summary>
167171
/// returns true if device was found
168172
/// </summary>
169-
public static bool TryGetMobileDevice(ReadOnlySpan<char> userAgent, [NotNullWhen(true)] out string? device)
173+
public static bool TryGetMobileDevice(string userAgent, [NotNullWhen(true)] out string? device)
170174
{
171175
device = GetMobileDevice(userAgent);
172176
return device is not null;

0 commit comments

Comments
 (0)