1
1
// Copyright (c) Six Labors.
2
2
// Licensed under the Six Labors Split License.
3
3
4
+ using System . Buffers ;
4
5
using SixLabors . ImageSharp . Formats ;
5
6
using SixLabors . ImageSharp . Memory ;
6
7
using SixLabors . ImageSharp . Metadata ;
@@ -67,20 +68,70 @@ private static IImageFormat InternalDetectFormat(Configuration configuration, St
67
68
int i ;
68
69
do
69
70
{
70
- i = stream . Read ( headersBuffer , n , headerSize - n ) ;
71
+ i = stream . Read ( headersBuffer [ n .. headerSize ] ) ;
71
72
n += i ;
72
73
}
73
74
while ( n < headerSize && i > 0 ) ;
74
75
75
76
stream . Position = startPosition ;
76
77
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
+ {
77
128
// Does the given stream contain enough data to fit in the header for the format
78
129
// and does that data match the format specification?
79
130
// Individual formats should still check since they are public.
80
131
IImageFormat ? format = null ;
81
132
foreach ( IImageFormatDetector formatDetector in configuration . ImageFormatsManager . FormatDetectors )
82
133
{
83
- if ( formatDetector . HeaderSize <= headerSize && formatDetector . TryDetectFormat ( headersBuffer , out IImageFormat ? attemptFormat ) )
134
+ if ( formatDetector . HeaderSize <= headersBuffer . Length && formatDetector . TryDetectFormat ( headersBuffer , out IImageFormat ? attemptFormat ) )
84
135
{
85
136
format = attemptFormat ;
86
137
}
@@ -106,6 +157,22 @@ private static IImageDecoder DiscoverDecoder(DecoderOptions options, Stream stre
106
157
return options . Configuration . ImageFormatsManager . GetDecoder ( format ) ;
107
158
}
108
159
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
+
109
176
/// <summary>
110
177
/// Decodes the image stream to the current image.
111
178
/// </summary>
@@ -122,14 +189,14 @@ private static Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream strea
122
189
return decoder . Decode < TPixel > ( options , stream ) ;
123
190
}
124
191
125
- private static Task < Image < TPixel > > DecodeAsync < TPixel > (
192
+ private static async Task < Image < TPixel > > DecodeAsync < TPixel > (
126
193
DecoderOptions options ,
127
194
Stream stream ,
128
195
CancellationToken cancellationToken )
129
196
where TPixel : unmanaged, IPixel < TPixel >
130
197
{
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 ) ;
133
200
}
134
201
135
202
private static Image Decode ( DecoderOptions options , Stream stream )
@@ -138,13 +205,13 @@ private static Image Decode(DecoderOptions options, Stream stream)
138
205
return decoder . Decode ( options , stream ) ;
139
206
}
140
207
141
- private static Task < Image > DecodeAsync (
208
+ private static async Task < Image > DecodeAsync (
142
209
DecoderOptions options ,
143
210
Stream stream ,
144
211
CancellationToken cancellationToken )
145
212
{
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 ) ;
148
215
}
149
216
150
217
/// <summary>
@@ -166,12 +233,12 @@ private static ImageInfo InternalIdentify(DecoderOptions options, Stream stream)
166
233
/// <param name="stream">The stream.</param>
167
234
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
168
235
/// <returns>The <see cref="ImageInfo"/>.</returns>
169
- private static Task < ImageInfo > InternalIdentifyAsync (
236
+ private static async Task < ImageInfo > InternalIdentifyAsync (
170
237
DecoderOptions options ,
171
238
Stream stream ,
172
239
CancellationToken cancellationToken )
173
240
{
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 ) ;
176
243
}
177
244
}
0 commit comments