Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(zip): enable ZipOuputStream to write precompressed files #683

Merged
merged 7 commits into from
Nov 17, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 28 additions & 3 deletions src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)
throw new InvalidOperationException("The Password property must be set before AES encrypted entries can be added");
}

entryIsPrecompressed = string.IsNullOrEmpty(Password) && method == CompressionMethod.Deflated && entry.Size >= 0 && entry.HasCrc && entry.CompressedSize >= 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems really volatile (and there are no tests for this).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way user can know compressed size and crc? Compressed size could be changed because of compression level or simply compressor logic change. So i think no one will pass it and expect that it will match internal deflator deflated size. Except if user wants to pass its own data

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So i think no one will pass it and expect

We can't consider this a non-breaking change just because it would seem illogical to use it in this way.
It's still the fact that code that put something in those properties for any reason (copying an entry object from another file, serialization, left-over code?) would suddenly stop working. At least it would throw an exception, but it's change of behavior which would warrant a new major release if done this way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is already broken because any fixes in compression engine will make the code fail as compressed size will differ.
Anyway, i got the point and reworked the PR


int compressionLevel = defaultCompressionLevel;

// Clear flags that the library manages internally
Expand All @@ -322,7 +324,7 @@ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)
bool headerInfoAvailable;

// No need to compress - definitely no data.
if (entry.Size == 0)
if (entry.Size == 0 && !entryIsPrecompressed)
{
entry.CompressedSize = entry.Size;
entry.Crc = 0;
Expand Down Expand Up @@ -406,14 +408,17 @@ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)

// Activate the entry.
curEntry = entry;
size = 0;

if(entryIsPrecompressed)
return;

crc.Reset();
if (method == CompressionMethod.Deflated)
{
deflater_.Reset();
deflater_.SetLevel(compressionLevel);
}
size = 0;

}

/// <summary>
Expand Down Expand Up @@ -506,6 +511,17 @@ internal void WriteEntryFooter(Stream stream)
throw new InvalidOperationException("No open entry");
}

if(entryIsPrecompressed)
{
if(curEntry.CompressedSize != size)
{
throw new ZipException($"compressed size was {size}, but {curEntry.CompressedSize} expected");
}

offset += size;
return;
}

long csize = size;

// First finish the deflater, if appropriate
Expand Down Expand Up @@ -695,6 +711,13 @@ public override void Write(byte[] buffer, int offset, int count)
throw new ArgumentException("Invalid offset/count combination");
}

if(entryIsPrecompressed)
{
size += count;
baseOutputStream_.Write(buffer, offset, count);
return;
}
piksel marked this conversation as resolved.
Show resolved Hide resolved

if (curEntry.AESKeySize == 0)
{
// Only update CRC if AES is not enabled
Expand Down Expand Up @@ -844,6 +867,8 @@ public override void Flush()
/// </summary>
private ZipEntry curEntry;

private bool entryIsPrecompressed;

private int defaultCompressionLevel = Deflater.DEFAULT_COMPRESSION;

private CompressionMethod curMethod = CompressionMethod.Deflated;
Expand Down