@@ -19,8 +19,7 @@ namespace SixLabors.ImageSharp.Web
1919 /// </summary>
2020 public sealed class FormatUtilities
2121 {
22- private readonly IImageFormat [ ] imageFormats ;
23- private readonly Dictionary < IImageFormat , string [ ] > fileExtensions = new Dictionary < IImageFormat , string [ ] > ( ) ;
22+ private readonly List < string > fileExtensions = new List < string > ( ) ;
2423 private readonly Dictionary < string , string > fileExtension = new Dictionary < string , string > ( ) ;
2524
2625 /// <summary>
@@ -33,12 +32,18 @@ public FormatUtilities(IOptions<ImageSharpMiddlewareOptions> options)
3332
3433 // The formats contained in the configuration are used a lot in hash generation
3534 // so we need them to be enumerated to remove allocations and allow indexing.
36- this . imageFormats = options . Value . Configuration . ImageFormats . ToArray ( ) ;
37- for ( int i = 0 ; i < this . imageFormats . Length ; i ++ )
35+ IImageFormat [ ] imageFormats = options . Value . Configuration . ImageFormats . ToArray ( ) ;
36+
37+ for ( int i = 0 ; i < imageFormats . Length ; i ++ )
3838 {
39- string [ ] extensions = this . imageFormats [ i ] . FileExtensions . ToArray ( ) ;
40- this . fileExtensions [ this . imageFormats [ i ] ] = extensions ;
41- this . fileExtension [ this . imageFormats [ i ] . DefaultMimeType ] = extensions [ 0 ] ;
39+ string [ ] extensions = imageFormats [ i ] . FileExtensions . ToArray ( ) ;
40+
41+ foreach ( string extension in extensions )
42+ {
43+ this . fileExtensions . Add ( extension ) ;
44+ }
45+
46+ this . fileExtension [ imageFormats [ i ] . DefaultMimeType ] = extensions [ 0 ] ;
4247 }
4348 }
4449
@@ -50,32 +55,46 @@ public FormatUtilities(IOptions<ImageSharpMiddlewareOptions> options)
5055 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
5156 public string GetExtensionFromUri ( string uri )
5257 {
53- // TODO: Investigate using span to reduce allocations here.
54- string [ ] parts = uri . Split ( '?' ) ;
55- if ( parts . Length > 1 && QueryHelpers . ParseQuery ( parts [ 1 ] ) . TryGetValue ( FormatWebProcessor . Format , out StringValues ext ) )
58+ int query = uri . IndexOf ( '?' ) ;
59+ ReadOnlySpan < char > path ;
60+
61+ if ( query > - 1 )
5662 {
57- return ext ;
63+ if ( uri . Contains ( FormatWebProcessor . Format , StringComparison . OrdinalIgnoreCase ) && QueryHelpers . ParseQuery ( uri . Substring ( query ) ) . TryGetValue ( FormatWebProcessor . Format , out StringValues ext ) )
64+ {
65+ return ext ;
66+ }
67+
68+ #if ! NETCOREAPP3_1_OR_GREATER
69+ path = uri . ToLowerInvariant ( ) . AsSpan ( 0 , query ) ;
70+ #else
71+ path = uri . AsSpan ( 0 , query ) ;
72+ #endif
73+ }
74+ else
75+ {
76+ #if ! NETCOREAPP3_1_OR_GREATER
77+ path = uri . ToLowerInvariant ( ) ;
78+ #else
79+ path = uri ;
80+ #endif
5881 }
5982
60- string path = parts [ 0 ] ;
61- string extension = null ;
62- int index = 0 ;
63- for ( int i = 0 ; i < this . imageFormats . Length ; i ++ )
83+ int extensionIndex ;
84+ if ( ( extensionIndex = path . LastIndexOf ( '.' ) ) != - 1 )
6485 {
65- for ( int j = 0 ; j < this . fileExtensions [ this . imageFormats [ i ] ] . Length ; j ++ )
86+ ReadOnlySpan < char > extension = path . Slice ( extensionIndex + 1 ) ;
87+
88+ foreach ( string ext in this . fileExtensions )
6689 {
67- int li = path . LastIndexOf ( $ ".{ this . fileExtensions [ this . imageFormats [ i ] ] [ j ] } ", StringComparison . OrdinalIgnoreCase ) ;
68- if ( li < index )
90+ if ( extension . Equals ( ext , StringComparison . OrdinalIgnoreCase ) )
6991 {
70- continue ;
92+ return ext ;
7193 }
72-
73- index = li ;
74- extension = this . fileExtensions [ this . imageFormats [ i ] ] [ j ] ;
7594 }
7695 }
7796
78- return extension ;
97+ return null ;
7998 }
8099
81100 /// <summary>
0 commit comments