33using ICSharpCode . SharpZipLib . Zip . Compression . Streams ;
44using System ;
55using System . IO ;
6- using System . Text ;
6+ using System . Linq ;
7+ using System . Threading ;
8+ using System . Threading . Tasks ;
79
810namespace ICSharpCode . SharpZipLib . GZip
911{
@@ -162,6 +164,26 @@ public override void Write(byte[] buffer, int offset, int count)
162164 base . Write ( buffer , offset , count ) ;
163165 }
164166
167+ #if NETSTANDARD2_1_OR_GREATER
168+ /// <inheritdoc cref="DeflaterOutputStream.WriteAsync(byte[],int,int,CancellationToken)"/>
169+ public override async Task WriteAsync ( byte [ ] buffer , int offset , int count , CancellationToken cancellationToken )
170+ {
171+
172+ if ( state_ == OutputState . Header )
173+ {
174+ await WriteHeaderAsync ( ) ;
175+ }
176+
177+ if ( state_ != OutputState . Footer )
178+ {
179+ throw new InvalidOperationException ( "Write not permitted in current state" ) ;
180+ }
181+
182+ crc . Update ( new ArraySegment < byte > ( buffer , offset , count ) ) ;
183+ await base . WriteAsync ( buffer , offset , count , cancellationToken ) ;
184+ }
185+ #endif
186+
165187 /// <summary>
166188 /// Writes remaining compressed output data to the output stream
167189 /// and closes it.
@@ -184,6 +206,30 @@ protected override void Dispose(bool disposing)
184206 }
185207 }
186208 }
209+
210+ #if NETSTANDARD2_1_OR_GREATER
211+ /// <inheritdoc cref="DeflaterOutputStream.DisposeAsync"/>
212+ public override async ValueTask DisposeAsync ( )
213+ {
214+ try
215+ {
216+ await FinishAsync ( CancellationToken . None ) ;
217+ }
218+ finally
219+ {
220+ if ( state_ != OutputState . Closed )
221+ {
222+ state_ = OutputState . Closed ;
223+ if ( IsStreamOwner )
224+ {
225+ await baseOutputStream_ . DisposeAsync ( ) ;
226+ }
227+ }
228+
229+ await base . DisposeAsync ( ) ;
230+ }
231+ }
232+ #endif
187233
188234 /// <summary>
189235 /// Flushes the stream by ensuring the header is written, and then calling <see cref="DeflaterOutputStream.Flush">Flush</see>
@@ -218,75 +264,117 @@ public override void Finish()
218264 {
219265 state_ = OutputState . Finished ;
220266 base . Finish ( ) ;
267+
268+ byte [ ] gzipFooter = GetFooter ( ) ;
221269
222- var totalin = ( uint ) ( deflater_ . TotalIn & 0xffffffff ) ;
223- var crcval = ( uint ) ( crc . Value & 0xffffffff ) ;
224-
225- byte [ ] gzipFooter ;
226-
227- unchecked
228- {
229- gzipFooter = new byte [ ] {
230- ( byte ) crcval , ( byte ) ( crcval >> 8 ) ,
231- ( byte ) ( crcval >> 16 ) , ( byte ) ( crcval >> 24 ) ,
270+ baseOutputStream_ . Write ( gzipFooter , 0 , gzipFooter . Length ) ;
271+ }
272+ }
232273
233- ( byte ) totalin , ( byte ) ( totalin >> 8 ) ,
234- ( byte ) ( totalin >> 16 ) , ( byte ) ( totalin >> 24 )
235- } ;
236- }
274+ #if NETSTANDARD2_1_OR_GREATER
275+ /// <inheritdoc cref="Finish"/>
276+ public override async Task FinishAsync ( CancellationToken ct )
277+ {
278+ // If no data has been written a header should be added.
279+ if ( state_ == OutputState . Header )
280+ {
281+ await WriteHeaderAsync ( ) ;
282+ }
237283
238- baseOutputStream_ . Write ( gzipFooter , 0 , gzipFooter . Length ) ;
284+ if ( state_ == OutputState . Footer )
285+ {
286+ state_ = OutputState . Finished ;
287+ await base . FinishAsync ( ct ) ;
288+ await baseOutputStream_ . WriteAsync ( GetFooter ( ) , ct ) ;
239289 }
240290 }
291+ #endif
241292
242293 #endregion DeflaterOutputStream overrides
243294
244295 #region Support Routines
245296
246- private static string CleanFilename ( string path )
247- => path . Substring ( path . LastIndexOf ( '/' ) + 1 ) ;
248-
249- private void WriteHeader ( )
297+ private byte [ ] GetFooter ( )
250298 {
251- if ( state_ == OutputState . Header )
252- {
253- state_ = OutputState . Footer ;
299+ var totalin = ( uint ) ( deflater_ . TotalIn & 0xffffffff ) ;
300+ var crcval = ( uint ) ( crc . Value & 0xffffffff ) ;
254301
255- var mod_time = ( int ) ( ( DateTime . Now . Ticks - new DateTime ( 1970 , 1 , 1 ) . Ticks ) / 10000000L ) ; // Ticks give back 100ns intervals
256- byte [ ] gzipHeader = {
257- // The two magic bytes
258- GZipConstants . ID1 ,
259- GZipConstants . ID2 ,
302+ byte [ ] gzipFooter ;
303+
304+ unchecked
305+ {
306+ gzipFooter = new [ ] {
307+ ( byte ) crcval ,
308+ ( byte ) ( crcval >> 8 ) ,
309+ ( byte ) ( crcval >> 16 ) ,
310+ ( byte ) ( crcval >> 24 ) ,
311+ ( byte ) totalin ,
312+ ( byte ) ( totalin >> 8 ) ,
313+ ( byte ) ( totalin >> 16 ) ,
314+ ( byte ) ( totalin >> 24 ) ,
315+ } ;
316+ }
260317
261- // The compression type
262- GZipConstants . CompressionMethodDeflate ,
318+ return gzipFooter ;
319+ }
263320
264- // The flags (not set)
265- ( byte ) flags ,
321+ private byte [ ] GetHeader ( )
322+ {
323+ var modTime = ( int ) ( ( DateTime . Now . Ticks - new DateTime ( 1970 , 1 , 1 ) . Ticks ) / 10000000L ) ; // Ticks give back 100ns intervals
324+ byte [ ] gzipHeader = {
325+ // The two magic bytes
326+ GZipConstants . ID1 ,
327+ GZipConstants . ID2 ,
266328
267- // The modification time
268- ( byte ) mod_time , ( byte ) ( mod_time >> 8 ) ,
269- ( byte ) ( mod_time >> 16 ) , ( byte ) ( mod_time >> 24 ) ,
329+ // The compression type
330+ GZipConstants . CompressionMethodDeflate ,
270331
271- // The extra flags
272- 0 ,
332+ // The flags (not set)
333+ ( byte ) flags ,
273334
274- // The OS type (unknown)
275- 255
276- } ;
335+ // The modification time
336+ ( byte ) modTime , ( byte ) ( modTime >> 8 ) ,
337+ ( byte ) ( modTime >> 16 ) , ( byte ) ( modTime >> 24 ) ,
277338
278- baseOutputStream_ . Write ( gzipHeader , 0 , gzipHeader . Length ) ;
339+ // The extra flags
340+ 0 ,
279341
280- if ( flags . HasFlag ( GZipFlags . FNAME ) )
281- {
282- var fname = GZipConstants . Encoding . GetBytes ( fileName ) ;
283- baseOutputStream_ . Write ( fname , 0 , fname . Length ) ;
342+ // The OS type (unknown)
343+ 255
344+ } ;
284345
285- // End filename string with a \0
286- baseOutputStream_ . Write ( new byte [ ] { 0 } , 0 , 1 ) ;
287- }
346+ if ( ! flags . HasFlag ( GZipFlags . FNAME ) )
347+ {
348+ return gzipHeader ;
288349 }
350+
351+
352+ return gzipHeader
353+ . Concat ( GZipConstants . Encoding . GetBytes ( fileName ) )
354+ . Concat ( new byte [ ] { 0 } ) // End filename string with a \0
355+ . ToArray ( ) ;
356+ }
357+
358+ private static string CleanFilename ( string path )
359+ => path . Substring ( path . LastIndexOf ( '/' ) + 1 ) ;
360+
361+ private void WriteHeader ( )
362+ {
363+ if ( state_ != OutputState . Header ) return ;
364+ state_ = OutputState . Footer ;
365+
366+ var gzipHeader = GetHeader ( ) ;
367+ baseOutputStream_ . Write ( gzipHeader , 0 , gzipHeader . Length ) ;
368+ }
369+
370+ #if NETSTANDARD2_1_OR_GREATER
371+ private async ValueTask WriteHeaderAsync ( )
372+ {
373+ if ( state_ != OutputState . Header ) return ;
374+ state_ = OutputState . Footer ;
375+ await baseOutputStream_ . WriteAsync ( GetHeader ( ) ) ;
289376 }
377+ #endif
290378
291379 #endregion Support Routines
292380 }
0 commit comments