@@ -270,7 +270,76 @@ public void PutNextEntry(ZipEntry entry)
270270 WriteOutput ( GetEntryEncryptionHeader ( entry ) ) ;
271271 }
272272 }
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+
274343 private void WriteOutput ( byte [ ] bytes )
275344 => baseOutputStream_ . Write ( bytes , 0 , bytes . Length ) ;
276345
@@ -282,7 +351,7 @@ private byte[] GetEntryEncryptionHeader(ZipEntry entry) =>
282351 ? InitializeAESPassword ( entry , Password )
283352 : CreateZipCryptoHeader ( entry . Crc < 0 ? entry . DosTime << 16 : entry . Crc ) ;
284353
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 )
286355 {
287356 if ( entry == null )
288357 {
@@ -313,6 +382,8 @@ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)
313382 throw new InvalidOperationException ( "The Password property must be set before AES encrypted entries can be added" ) ;
314383 }
315384
385+ entryIsPassthrough = passthroughEntry ;
386+
316387 int compressionLevel = defaultCompressionLevel ;
317388
318389 // Clear flags that the library manages internally
@@ -322,7 +393,7 @@ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)
322393 bool headerInfoAvailable ;
323394
324395 // No need to compress - definitely no data.
325- if ( entry . Size == 0 )
396+ if ( entry . Size == 0 && ! entryIsPassthrough )
326397 {
327398 entry . CompressedSize = entry . Size ;
328399 entry . Crc = 0 ;
@@ -406,14 +477,17 @@ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)
406477
407478 // Activate the entry.
408479 curEntry = entry ;
480+ size = 0 ;
481+
482+ if ( entryIsPassthrough )
483+ return ;
484+
409485 crc . Reset ( ) ;
410486 if ( method == CompressionMethod . Deflated )
411487 {
412488 deflater_ . Reset ( ) ;
413489 deflater_ . SetLevel ( compressionLevel ) ;
414490 }
415- size = 0 ;
416-
417491 }
418492
419493 /// <summary>
@@ -506,6 +580,17 @@ internal void WriteEntryFooter(Stream stream)
506580 throw new InvalidOperationException ( "No open entry" ) ;
507581 }
508582
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+
509594 long csize = size ;
510595
511596 // First finish the deflater, if appropriate
@@ -695,30 +780,28 @@ public override void Write(byte[] buffer, int offset, int count)
695780 throw new ArgumentException ( "Invalid offset/count combination" ) ;
696781 }
697782
698- if ( curEntry . AESKeySize == 0 )
783+ if ( curEntry . AESKeySize == 0 && ! entryIsPassthrough )
699784 {
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
701786 crc . Update ( new ArraySegment < byte > ( buffer , offset , count ) ) ;
702787 }
703788
704789 size += count ;
705790
706- switch ( curMethod )
791+ if ( curMethod == CompressionMethod . Stored || entryIsPassthrough )
707792 {
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 ) ;
722805 }
723806 }
724807
@@ -844,6 +927,8 @@ public override void Flush()
844927 /// </summary>
845928 private ZipEntry curEntry ;
846929
930+ private bool entryIsPassthrough ;
931+
847932 private int defaultCompressionLevel = Deflater . DEFAULT_COMPRESSION ;
848933
849934 private CompressionMethod curMethod = CompressionMethod . Deflated ;
0 commit comments