11// Copyright (c) Six Labors.
22// Licensed under the Six Labors Split License.
33
4+ using System . Buffers ;
45using SixLabors . ImageSharp . Formats ;
56using SixLabors . ImageSharp . Memory ;
67using SixLabors . ImageSharp . Metadata ;
@@ -67,20 +68,70 @@ private static IImageFormat InternalDetectFormat(Configuration configuration, St
6768 int i ;
6869 do
6970 {
70- i = stream . Read ( headersBuffer , n , headerSize - n ) ;
71+ i = stream . Read ( headersBuffer [ n .. headerSize ] ) ;
7172 n += i ;
7273 }
7374 while ( n < headerSize && i > 0 ) ;
7475
7576 stream . Position = startPosition ;
7677
78+ return InternalDetectFormat ( configuration , headersBuffer [ ..n ] ) ;
79+ }
80+
81+ /// <summary>
82+ /// By reading the header on the provided stream this calculates the images format.
83+ /// </summary>
84+ /// <param name="configuration">The general configuration.</param>
85+ /// <param name="stream">The image stream to read the header from.</param>
86+ /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
87+ /// <returns>The mime type or null if none found.</returns>
88+ /// <exception cref="UnknownImageFormatException">The input format is not recognized.</exception>
89+ private static async ValueTask < IImageFormat > InternalDetectFormatAsync (
90+ Configuration configuration ,
91+ Stream stream ,
92+ CancellationToken cancellationToken )
93+ {
94+ // We take a minimum of the stream length vs the max header size and always check below
95+ // to ensure that only formats that headers fit within the given buffer length are tested.
96+ int headerSize = ( int ) Math . Min ( configuration . MaxHeaderSize , stream . Length ) ;
97+ if ( headerSize <= 0 )
98+ {
99+ ImageFormatManager . ThrowInvalidDecoder ( configuration . ImageFormatsManager ) ;
100+ }
101+
102+ using ( IMemoryOwner < byte > memoryOwner = configuration . MemoryAllocator . Allocate < byte > ( headerSize ) )
103+ {
104+ Memory < byte > headersBuffer = memoryOwner . Memory ;
105+ long startPosition = stream . Position ;
106+
107+ // Read doesn't always guarantee the full returned length so read a byte
108+ // at a time until we get either our count or hit the end of the stream.
109+ int n = 0 ;
110+ int i ;
111+ do
112+ {
113+ i = await stream . ReadAsync ( headersBuffer [ n ..headerSize ] , cancellationToken ) ;
114+ n += i ;
115+ }
116+ while ( n < headerSize && i > 0 ) ;
117+
118+ stream . Position = startPosition ;
119+
120+ return InternalDetectFormat ( configuration , headersBuffer . Span [ ..n ] ) ;
121+ }
122+ }
123+
124+ private static IImageFormat InternalDetectFormat (
125+ Configuration configuration ,
126+ ReadOnlySpan < byte > headersBuffer )
127+ {
77128 // Does the given stream contain enough data to fit in the header for the format
78129 // and does that data match the format specification?
79130 // Individual formats should still check since they are public.
80131 IImageFormat ? format = null ;
81132 foreach ( IImageFormatDetector formatDetector in configuration . ImageFormatsManager . FormatDetectors )
82133 {
83- if ( formatDetector . HeaderSize <= headerSize && formatDetector . TryDetectFormat ( headersBuffer , out IImageFormat ? attemptFormat ) )
134+ if ( formatDetector . HeaderSize <= headersBuffer . Length && formatDetector . TryDetectFormat ( headersBuffer , out IImageFormat ? attemptFormat ) )
84135 {
85136 format = attemptFormat ;
86137 }
@@ -106,6 +157,22 @@ private static IImageDecoder DiscoverDecoder(DecoderOptions options, Stream stre
106157 return options . Configuration . ImageFormatsManager . GetDecoder ( format ) ;
107158 }
108159
160+ /// <summary>
161+ /// By reading the header on the provided stream this calculates the images format.
162+ /// </summary>
163+ /// <param name="options">The general decoder options.</param>
164+ /// <param name="stream">The image stream to read the header from.</param>
165+ /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
166+ /// <returns>The <see cref="IImageDecoder"/>.</returns>
167+ private static async ValueTask < IImageDecoder > DiscoverDecoderAsync (
168+ DecoderOptions options ,
169+ Stream stream ,
170+ CancellationToken cancellationToken )
171+ {
172+ IImageFormat format = await InternalDetectFormatAsync ( options . Configuration , stream , cancellationToken ) ;
173+ return options . Configuration . ImageFormatsManager . GetDecoder ( format ) ;
174+ }
175+
109176 /// <summary>
110177 /// Decodes the image stream to the current image.
111178 /// </summary>
@@ -122,14 +189,14 @@ private static Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream strea
122189 return decoder . Decode < TPixel > ( options , stream ) ;
123190 }
124191
125- private static Task < Image < TPixel > > DecodeAsync < TPixel > (
192+ private static async Task < Image < TPixel > > DecodeAsync < TPixel > (
126193 DecoderOptions options ,
127194 Stream stream ,
128195 CancellationToken cancellationToken )
129196 where TPixel : unmanaged, IPixel < TPixel >
130197 {
131- IImageDecoder decoder = DiscoverDecoder ( options , stream ) ;
132- return decoder . DecodeAsync < TPixel > ( options , stream , cancellationToken ) ;
198+ IImageDecoder decoder = await DiscoverDecoderAsync ( options , stream , cancellationToken ) ;
199+ return await decoder . DecodeAsync < TPixel > ( options , stream , cancellationToken ) ;
133200 }
134201
135202 private static Image Decode ( DecoderOptions options , Stream stream )
@@ -138,13 +205,13 @@ private static Image Decode(DecoderOptions options, Stream stream)
138205 return decoder . Decode ( options , stream ) ;
139206 }
140207
141- private static Task < Image > DecodeAsync (
208+ private static async Task < Image > DecodeAsync (
142209 DecoderOptions options ,
143210 Stream stream ,
144211 CancellationToken cancellationToken )
145212 {
146- IImageDecoder decoder = DiscoverDecoder ( options , stream ) ;
147- return decoder . DecodeAsync ( options , stream , cancellationToken ) ;
213+ IImageDecoder decoder = await DiscoverDecoderAsync ( options , stream , cancellationToken ) ;
214+ return await decoder . DecodeAsync ( options , stream , cancellationToken ) ;
148215 }
149216
150217 /// <summary>
@@ -166,12 +233,12 @@ private static ImageInfo InternalIdentify(DecoderOptions options, Stream stream)
166233 /// <param name="stream">The stream.</param>
167234 /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
168235 /// <returns>The <see cref="ImageInfo"/>.</returns>
169- private static Task < ImageInfo > InternalIdentifyAsync (
236+ private static async Task < ImageInfo > InternalIdentifyAsync (
170237 DecoderOptions options ,
171238 Stream stream ,
172239 CancellationToken cancellationToken )
173240 {
174- IImageDecoder decoder = DiscoverDecoder ( options , stream ) ;
175- return decoder . IdentifyAsync ( options , stream , cancellationToken ) ;
241+ IImageDecoder decoder = await DiscoverDecoderAsync ( options , stream , cancellationToken ) ;
242+ return await decoder . IdentifyAsync ( options , stream , cancellationToken ) ;
176243 }
177244}
0 commit comments