@@ -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