@@ -270,7 +270,76 @@ public void PutNextEntry(ZipEntry entry)
270
270
WriteOutput ( GetEntryEncryptionHeader ( entry ) ) ;
271
271
}
272
272
}
273
-
273
+
274
+ /// <summary>
275
+ /// Starts a new passthrough Zip entry. It automatically closes the previous
276
+ /// entry if present.
277
+ /// Passthrough entry is an entry that is created from compressed data.
278
+ /// It is useful to avoid recompression to save CPU resources if compressed data is already disposable.
279
+ /// All entry elements bar name, crc, size and compressed size are optional, but must be correct if present.
280
+ /// Compression should be set to Deflated.
281
+ /// </summary>
282
+ /// <param name="entry">
283
+ /// the entry.
284
+ /// </param>
285
+ /// <exception cref="System.ArgumentNullException">
286
+ /// if entry passed is null.
287
+ /// </exception>
288
+ /// <exception cref="System.IO.IOException">
289
+ /// if an I/O error occurred.
290
+ /// </exception>
291
+ /// <exception cref="System.InvalidOperationException">
292
+ /// if stream was finished.
293
+ /// </exception>
294
+ /// <exception cref="ZipException">
295
+ /// Crc is not set<br/>
296
+ /// Size is not set<br/>
297
+ /// CompressedSize is not set<br/>
298
+ /// CompressionMethod is not Deflate<br/>
299
+ /// Too many entries in the Zip file<br/>
300
+ /// Entry name is too long<br/>
301
+ /// Finish has already been called<br/>
302
+ /// </exception>
303
+ /// <exception cref="System.NotImplementedException">
304
+ /// The Compression method specified for the entry is unsupported<br/>
305
+ /// Entry is encrypted<br/>
306
+ /// </exception>
307
+ public void PutNextPassthroughEntry ( ZipEntry entry )
308
+ {
309
+ if ( curEntry != null )
310
+ {
311
+ CloseEntry ( ) ;
312
+ }
313
+
314
+ if ( entry . Crc < 0 )
315
+ {
316
+ throw new ZipException ( "Crc must be set for passthrough entry" ) ;
317
+ }
318
+
319
+ if ( entry . Size < 0 )
320
+ {
321
+ throw new ZipException ( "Size must be set for passthrough entry" ) ;
322
+ }
323
+
324
+ if ( entry . CompressedSize < 0 )
325
+ {
326
+ throw new ZipException ( "CompressedSize must be set for passthrough entry" ) ;
327
+ }
328
+
329
+ if ( entry . CompressionMethod != CompressionMethod . Deflated )
330
+ {
331
+ throw new NotImplementedException ( "Only Deflated entries are supported for passthrough" ) ;
332
+ }
333
+
334
+ if ( ! string . IsNullOrEmpty ( Password ) )
335
+ {
336
+ throw new NotImplementedException ( "Encrypted passthrough entries are not supported" ) ;
337
+ }
338
+
339
+ PutNextEntry ( baseOutputStream_ , entry , 0 , true ) ;
340
+ }
341
+
342
+
274
343
private void WriteOutput ( byte [ ] bytes )
275
344
=> baseOutputStream_ . Write ( bytes , 0 , bytes . Length ) ;
276
345
@@ -282,7 +351,7 @@ private byte[] GetEntryEncryptionHeader(ZipEntry entry) =>
282
351
? InitializeAESPassword ( entry , Password )
283
352
: CreateZipCryptoHeader ( entry . Crc < 0 ? entry . DosTime << 16 : entry . Crc ) ;
284
353
285
- internal void PutNextEntry ( Stream stream , ZipEntry entry , long streamOffset = 0 )
354
+ internal void PutNextEntry ( Stream stream , ZipEntry entry , long streamOffset = 0 , bool passthroughEntry = false )
286
355
{
287
356
if ( entry == null )
288
357
{
@@ -313,6 +382,8 @@ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)
313
382
throw new InvalidOperationException ( "The Password property must be set before AES encrypted entries can be added" ) ;
314
383
}
315
384
385
+ entryIsPassthrough = passthroughEntry ;
386
+
316
387
int compressionLevel = defaultCompressionLevel ;
317
388
318
389
// Clear flags that the library manages internally
@@ -322,7 +393,7 @@ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)
322
393
bool headerInfoAvailable ;
323
394
324
395
// No need to compress - definitely no data.
325
- if ( entry . Size == 0 )
396
+ if ( entry . Size == 0 && ! entryIsPassthrough )
326
397
{
327
398
entry . CompressedSize = entry . Size ;
328
399
entry . Crc = 0 ;
@@ -406,14 +477,17 @@ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)
406
477
407
478
// Activate the entry.
408
479
curEntry = entry ;
480
+ size = 0 ;
481
+
482
+ if ( entryIsPassthrough )
483
+ return ;
484
+
409
485
crc . Reset ( ) ;
410
486
if ( method == CompressionMethod . Deflated )
411
487
{
412
488
deflater_ . Reset ( ) ;
413
489
deflater_ . SetLevel ( compressionLevel ) ;
414
490
}
415
- size = 0 ;
416
-
417
491
}
418
492
419
493
/// <summary>
@@ -506,6 +580,17 @@ internal void WriteEntryFooter(Stream stream)
506
580
throw new InvalidOperationException ( "No open entry" ) ;
507
581
}
508
582
583
+ if ( entryIsPassthrough )
584
+ {
585
+ if ( curEntry . CompressedSize != size )
586
+ {
587
+ throw new ZipException ( $ "compressed size was { size } , but { curEntry . CompressedSize } expected") ;
588
+ }
589
+
590
+ offset += size ;
591
+ return ;
592
+ }
593
+
509
594
long csize = size ;
510
595
511
596
// First finish the deflater, if appropriate
@@ -695,30 +780,28 @@ public override void Write(byte[] buffer, int offset, int count)
695
780
throw new ArgumentException ( "Invalid offset/count combination" ) ;
696
781
}
697
782
698
- if ( curEntry . AESKeySize == 0 )
783
+ if ( curEntry . AESKeySize == 0 && ! entryIsPassthrough )
699
784
{
700
- // Only update CRC if AES is not enabled
785
+ // Only update CRC if AES is not enabled and entry is not a passthrough one
701
786
crc . Update ( new ArraySegment < byte > ( buffer , offset , count ) ) ;
702
787
}
703
788
704
789
size += count ;
705
790
706
- switch ( curMethod )
791
+ if ( curMethod == CompressionMethod . Stored || entryIsPassthrough )
707
792
{
708
- case CompressionMethod . Deflated :
709
- base . Write ( buffer , offset , count ) ;
710
- break ;
711
-
712
- case CompressionMethod . Stored :
713
- if ( Password != null )
714
- {
715
- CopyAndEncrypt ( buffer , offset , count ) ;
716
- }
717
- else
718
- {
719
- baseOutputStream_ . Write ( buffer , offset , count ) ;
720
- }
721
- break ;
793
+ if ( Password != null )
794
+ {
795
+ CopyAndEncrypt ( buffer , offset , count ) ;
796
+ }
797
+ else
798
+ {
799
+ baseOutputStream_ . Write ( buffer , offset , count ) ;
800
+ }
801
+ }
802
+ else
803
+ {
804
+ base . Write ( buffer , offset , count ) ;
722
805
}
723
806
}
724
807
@@ -844,6 +927,8 @@ public override void Flush()
844
927
/// </summary>
845
928
private ZipEntry curEntry ;
846
929
930
+ private bool entryIsPassthrough ;
931
+
847
932
private int defaultCompressionLevel = Deflater . DEFAULT_COMPRESSION ;
848
933
849
934
private CompressionMethod curMethod = CompressionMethod . Deflated ;
0 commit comments