Skip to content

Commit 324f1a8

Browse files
committed
Removed bound checks in TryExtractVersion
1 parent 1ae2901 commit 324f1a8

File tree

1 file changed

+20
-26
lines changed

1 file changed

+20
-26
lines changed

src/HttpUserAgentParser/HttpUserAgentParser.cs

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,10 @@ public static (string Name, string? Version)? GetBrowser(string userAgent)
105105
}
106106

107107
string? version = null;
108-
if (TryExtractVersion(ua, versionSearchStart, out Range range))
108+
ua = ua.Slice(versionSearchStart);
109+
if (TryExtractVersion(ua, out Range range))
109110
{
110-
version = userAgent.AsSpan(range.Start.Value, range.End.Value - range.Start.Value).ToString();
111+
version = ua[range].ToString();
111112
}
112113

113114
return (browserRule.Name, version);
@@ -176,60 +177,53 @@ public static bool TryGetMobileDevice(string userAgent, [NotNullWhen(true)] out
176177
}
177178

178179
[MethodImpl(MethodImplOptions.AggressiveInlining)]
179-
private static bool ContainsIgnoreCase(ReadOnlySpan<char> haystack, string needle)
180+
private static bool ContainsIgnoreCase(ReadOnlySpan<char> haystack, ReadOnlySpan<char> needle)
180181
=> TryIndexOf(haystack, needle, out _);
181182

182183
[MethodImpl(MethodImplOptions.AggressiveInlining)]
183-
private static bool TryIndexOf(ReadOnlySpan<char> haystack, string needle, out int index)
184+
private static bool TryIndexOf(ReadOnlySpan<char> haystack, ReadOnlySpan<char> needle, out int index)
184185
{
185-
index = haystack.IndexOf(needle.AsSpan(), StringComparison.OrdinalIgnoreCase);
186+
index = haystack.IndexOf(needle, StringComparison.OrdinalIgnoreCase);
186187
return index >= 0;
187188
}
188189

189190
/// <summary>
190-
/// Extracts a dotted numeric version starting at or after <paramref name="startIndex"/>.
191+
/// Extracts a dotted numeric version.
191192
/// Accepts digits and dots; skips common separators ('/', ' ', ':', '=') until first digit.
192193
/// Returns false if no version-like token is found.
193194
/// </summary>
194-
private static bool TryExtractVersion(ReadOnlySpan<char> haystack, int startIndex, out Range range)
195+
private static bool TryExtractVersion(ReadOnlySpan<char> haystack, out Range range)
195196
{
196197
range = default;
197-
if ((uint)startIndex >= (uint)haystack.Length)
198-
{
199-
return false;
200-
}
201198

202199
// Limit search window to avoid scanning entire UA string unnecessarily
203-
const int window = 128;
204-
int end = Math.Min(haystack.Length, startIndex + window);
205-
int i = startIndex;
200+
const int Window = 128;
201+
if (haystack.Length >= Window)
202+
{
203+
haystack = haystack.Slice(0, Window);
204+
}
206205

207-
// Skip separators until we hit a digit
208-
while (i < end)
206+
int i = 0;
207+
for (; i < haystack.Length; ++i)
209208
{
210209
char c = haystack[i];
211-
if ((uint)(c - '0') <= 9)
210+
if (char.IsBetween(c, '0', '9'))
212211
{
213212
break;
214213
}
215-
i++;
216-
}
217-
218-
if (i >= end)
219-
{
220-
return false;
221214
}
222215

223216
int s = i;
224-
while (i < end)
217+
haystack = haystack.Slice(i + 1);
218+
for (i = 0; i < haystack.Length; ++i)
225219
{
226220
char c = haystack[i];
227-
if (!((uint)(c - '0') <= 9 || c == '.'))
221+
if (!(char.IsBetween(c, '0', '9') || c == '.'))
228222
{
229223
break;
230224
}
231-
i++;
232225
}
226+
i += s + 1; // shift back the previous domain
233227

234228
if (i == s)
235229
{

0 commit comments

Comments
 (0)