Skip to content

Commit a23f6a2

Browse files
committed
refactor(parser): update user agent parsing for zero allocations
1 parent 240783a commit a23f6a2

File tree

3 files changed

+38
-14
lines changed

3 files changed

+38
-14
lines changed

.vscode/tasks.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "test",
6+
"type": "shell",
7+
"command": "dotnet test --nologo",
8+
"args": [],
9+
"problemMatcher": [
10+
"$msCompile"
11+
],
12+
"group": "build"
13+
},
14+
{
15+
"label": "test",
16+
"type": "shell",
17+
"command": "dotnet test --nologo",
18+
"args": [],
19+
"problemMatcher": [
20+
"$msCompile"
21+
],
22+
"group": "build"
23+
}
24+
]
25+
}

src/HttpUserAgentParser/HttpUserAgentParser.cs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,26 @@ public static class HttpUserAgentParser
1414

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

24-
// analyze
25-
if (TryGetRobot(userAgent, out string? robotName))
23+
if (TryGetRobot(span, out string? robotName))
2624
{
27-
return HttpUserAgentInformation.CreateForRobot(userAgent.ToString(), robotName);
25+
return HttpUserAgentInformation.CreateForRobot(userAgent, robotName);
2826
}
2927

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

33-
if (TryGetBrowser(userAgent, out (string Name, string? Version)? browser))
31+
if (TryGetBrowser(span, out (string Name, string? Version)? browser))
3432
{
35-
return HttpUserAgentInformation.CreateForBrowser(userAgent.ToString(), platform, browser?.Name, browser?.Version, mobileDeviceType);
33+
return HttpUserAgentInformation.CreateForBrowser(userAgent, platform, browser?.Name, browser?.Version, mobileDeviceType);
3634
}
3735

38-
return HttpUserAgentInformation.CreateForUnknown(userAgent.ToString(), platform, mobileDeviceType);
36+
return HttpUserAgentInformation.CreateForUnknown(userAgent, platform, mobileDeviceType);
3937
}
4038

4139
/// <summary>
@@ -105,7 +103,8 @@ public static (string Name, string? Version)? GetBrowser(ReadOnlySpan<char> user
105103
string? version = null;
106104
if (TryExtractVersion(userAgent, versionSearchStart, out Range range))
107105
{
108-
version = userAgent.ToString();
106+
// Only allocate the version substring, not the whole user agent
107+
version = userAgent[range].ToString();
109108
}
110109

111110
return (rule.Name, version);

src/HttpUserAgentParser/Providers/HttpUserAgentParserDefaultProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace MyCSharp.HttpUserAgentParser.Providers;
88
public class HttpUserAgentParserDefaultProvider : IHttpUserAgentParserProvider
99
{
1010
/// <summary>
11-
/// returns the result of <see cref="HttpUserAgentParser.Parse"/>
11+
/// returns the result of <see cref="HttpUserAgentParser.Parse(string)"/>
1212
/// </summary>
1313
public HttpUserAgentInformation Parse(string userAgent)
1414
=> HttpUserAgentParser.Parse(userAgent);

0 commit comments

Comments
 (0)