@@ -138,6 +138,11 @@ public string FileName
138138 }
139139 }
140140
141+ /// <summary>
142+ /// If defined, will use this time instead of the current for the output header
143+ /// </summary>
144+ public DateTime ? ModifiedTime { get ; set ; }
145+
141146 #endregion Public API
142147
143148 #region Stream overrides
@@ -149,21 +154,47 @@ public string FileName
149154 /// <param name="offset">Offset of first byte in buf to write</param>
150155 /// <param name="count">Number of bytes to write</param>
151156 public override void Write ( byte [ ] buffer , int offset , int count )
157+ => WriteSyncOrAsync ( buffer , offset , count , null ) . GetAwaiter ( ) . GetResult ( ) ;
158+
159+ private async Task WriteSyncOrAsync ( byte [ ] buffer , int offset , int count , CancellationToken ? ct )
152160 {
153161 if ( state_ == OutputState . Header )
154162 {
155- WriteHeader ( ) ;
163+ if ( ct . HasValue )
164+ {
165+ await WriteHeaderAsync ( ct . Value ) . ConfigureAwait ( false ) ;
166+ }
167+ else
168+ {
169+ WriteHeader ( ) ;
170+ }
156171 }
157172
158173 if ( state_ != OutputState . Footer )
159- {
160174 throw new InvalidOperationException ( "Write not permitted in current state" ) ;
161- }
162-
175+
163176 crc . Update ( new ArraySegment < byte > ( buffer , offset , count ) ) ;
164- base . Write ( buffer , offset , count ) ;
177+
178+ if ( ct . HasValue )
179+ {
180+ await base . WriteAsync ( buffer , offset , count , ct . Value ) . ConfigureAwait ( false ) ;
181+ }
182+ else
183+ {
184+ base . Write ( buffer , offset , count ) ;
185+ }
165186 }
166187
188+ /// <summary>
189+ /// Asynchronously write given buffer to output updating crc
190+ /// </summary>
191+ /// <param name="buffer">Buffer to write</param>
192+ /// <param name="offset">Offset of first byte in buf to write</param>
193+ /// <param name="count">Number of bytes to write</param>
194+ /// <param name="ct">The token to monitor for cancellation requests</param>
195+ public override async Task WriteAsync ( byte [ ] buffer , int offset , int count , CancellationToken ct )
196+ => await WriteSyncOrAsync ( buffer , offset , count , ct ) . ConfigureAwait ( false ) ;
197+
167198 /// <summary>
168199 /// Writes remaining compressed output data to the output stream
169200 /// and closes it.
@@ -187,7 +218,7 @@ protected override void Dispose(bool disposing)
187218 }
188219 }
189220
190- #if NETSTANDARD2_1_OR_GREATER
221+ #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
191222 /// <inheritdoc cref="DeflaterOutputStream.Dispose"/>
192223 public override async ValueTask DisposeAsync ( )
193224 {
@@ -225,6 +256,16 @@ public override void Flush()
225256 base . Flush ( ) ;
226257 }
227258
259+ /// <inheritdoc cref="Flush"/>
260+ public override async Task FlushAsync ( CancellationToken ct )
261+ {
262+ if ( state_ == OutputState . Header )
263+ {
264+ await WriteHeaderAsync ( ct ) . ConfigureAwait ( false ) ;
265+ }
266+ await base . FlushAsync ( ct ) . ConfigureAwait ( false ) ;
267+ }
268+
228269 #endregion Stream overrides
229270
230271 #region DeflaterOutputStream overrides
@@ -249,21 +290,13 @@ public override void Finish()
249290 }
250291 }
251292
252- /// <inheritdoc cref="Flush"/>
253- public override async Task FlushAsync ( CancellationToken ct )
254- {
255- await WriteHeaderAsync ( ) . ConfigureAwait ( false ) ;
256- await base . FlushAsync ( ct ) . ConfigureAwait ( false ) ;
257- }
258-
259-
260293 /// <inheritdoc cref="Finish"/>
261294 public override async Task FinishAsync ( CancellationToken ct )
262295 {
263296 // If no data has been written a header should be added.
264297 if ( state_ == OutputState . Header )
265298 {
266- await WriteHeaderAsync ( ) . ConfigureAwait ( false ) ;
299+ await WriteHeaderAsync ( ct ) . ConfigureAwait ( false ) ;
267300 }
268301
269302 if ( state_ == OutputState . Footer )
@@ -305,7 +338,8 @@ private byte[] GetFooter()
305338
306339 private byte [ ] GetHeader ( )
307340 {
308- var modTime = ( int ) ( ( DateTime . Now . Ticks - new DateTime ( 1970 , 1 , 1 ) . Ticks ) / 10000000L ) ; // Ticks give back 100ns intervals
341+ var modifiedUtc = ModifiedTime ? . ToUniversalTime ( ) ?? DateTime . UtcNow ;
342+ var modTime = ( int ) ( ( modifiedUtc - new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 , DateTimeKind . Utc ) ) . Ticks / 10000000L ) ; // Ticks give back 100ns intervals
309343 byte [ ] gzipHeader = {
310344 // The two magic bytes
311345 GZipConstants . ID1 ,
@@ -351,12 +385,12 @@ private void WriteHeader()
351385 baseOutputStream_ . Write ( gzipHeader , 0 , gzipHeader . Length ) ;
352386 }
353387
354- private async Task WriteHeaderAsync ( )
388+ private async Task WriteHeaderAsync ( CancellationToken ct )
355389 {
356390 if ( state_ != OutputState . Header ) return ;
357391 state_ = OutputState . Footer ;
358392 var gzipHeader = GetHeader ( ) ;
359- await baseOutputStream_ . WriteAsync ( gzipHeader , 0 , gzipHeader . Length ) . ConfigureAwait ( false ) ;
393+ await baseOutputStream_ . WriteAsync ( gzipHeader , 0 , gzipHeader . Length , ct ) . ConfigureAwait ( false ) ;
360394 }
361395
362396 #endregion Support Routines
0 commit comments