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

Add async support on ZipOutputStream #547

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2;net45</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netstandard2.1;net45</TargetFrameworks>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>../../assets/ICSharpCode.SharpZipLib.snk</AssemblyOriginatorKeyFile>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down Expand Up @@ -40,5 +40,5 @@ Please see https://github.com/icsharpcode/SharpZipLib/wiki/Release-1.3.1 for mor
<PackagePath>images</PackagePath>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;

namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
{
Expand Down Expand Up @@ -131,6 +133,50 @@ public virtual void Finish()
}
}

/// <summary>
/// Finishes the stream by calling finish() on the deflater.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
/// <exception cref="SharpZipBaseException">
/// Not all input is deflated
/// </exception>
public virtual async Task FinishAsync(CancellationToken cancellationToken)
{
deflater_.Finish();
while (!deflater_.IsFinished)
{
int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
if (len <= 0)
{
break;
}

if (cryptoTransform_ != null)
{
EncryptBlock(buffer_, 0, len);
}

await baseOutputStream_.WriteAsync(buffer_, 0, len, cancellationToken);
}

if (!deflater_.IsFinished)
{
throw new SharpZipBaseException("Can't deflate all input?");
}

await baseOutputStream_.FlushAsync(cancellationToken);

if (cryptoTransform_ != null)
{
if (cryptoTransform_ is ZipAESTransform)
{
AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
}
cryptoTransform_.Dispose();
cryptoTransform_ = null;
}
}

/// <summary>
/// Gets or sets a flag indicating ownership of underlying stream.
/// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
Expand Down Expand Up @@ -419,6 +465,38 @@ protected override void Dispose(bool disposing)
}
}

#if NETSTANDARD2_1
/// <summary>
/// Calls <see cref="FinishAsync"/> and closes the underlying
/// stream when <see cref="IsStreamOwner"></see> is true.
/// </summary>
public override async ValueTask DisposeAsync()
{
if (!isClosed_)
{
isClosed_ = true;

try
{
await FinishAsync(CancellationToken.None);
if (cryptoTransform_ != null)
{
GetAuthCodeIfAES();
cryptoTransform_.Dispose();
cryptoTransform_ = null;
}
}
finally
{
if (IsStreamOwner)
{
await baseOutputStream_.DisposeAsync();
}
}
}
}
#endif

/// <summary>
/// Get the Auth code for AES encrypted entries
/// </summary>
Expand Down
172 changes: 172 additions & 0 deletions src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace ICSharpCode.SharpZipLib.Zip
{
Expand Down Expand Up @@ -361,6 +363,41 @@ public void WriteZip64EndOfCentralDirectory(long noOfEntries, long sizeEntries,
WriteLEInt(1);
}

/// <summary>
/// Write Zip64 end of central directory records (File header and locator).
/// </summary>
/// <param name="noOfEntries">The number of entries in the central directory.</param>
/// <param name="sizeEntries">The size of entries in the central directory.</param>
/// <param name="centralDirOffset">The offset of the central directory.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
public async Task WriteZip64EndOfCentralDirectoryAsync(long noOfEntries, long sizeEntries, long centralDirOffset, CancellationToken cancellationToken)
{
long centralSignatureOffset = centralDirOffset + sizeEntries;
await WriteLEIntAsync(ZipConstants.Zip64CentralFileHeaderSignature, cancellationToken);
await WriteLELongAsync(44, cancellationToken); // Size of this record (total size of remaining fields in header or full size - 12)
await WriteLEShortAsync(ZipConstants.VersionMadeBy, cancellationToken); // Version made by
await WriteLEShortAsync(ZipConstants.VersionZip64, cancellationToken); // Version to extract
await WriteLEIntAsync(0, cancellationToken); // Number of this disk
await WriteLEIntAsync(0, cancellationToken); // number of the disk with the start of the central directory
await WriteLELongAsync(noOfEntries, cancellationToken); // No of entries on this disk
await WriteLELongAsync(noOfEntries, cancellationToken); // Total No of entries in central directory
await WriteLELongAsync(sizeEntries, cancellationToken); // Size of the central directory
await WriteLELongAsync(centralDirOffset, cancellationToken); // offset of start of central directory
// zip64 extensible data sector not catered for here (variable size)

// Write the Zip64 end of central directory locator
await WriteLEIntAsync(ZipConstants.Zip64CentralDirLocatorSignature, cancellationToken);

// no of the disk with the start of the zip64 end of central directory
await WriteLEIntAsync(0, cancellationToken);

// relative offset of the zip64 end of central directory record
await WriteLELongAsync(centralSignatureOffset, cancellationToken);

// total number of disks
await WriteLEIntAsync(1, cancellationToken);
}

/// <summary>
/// Write the required records to end the central directory.
/// </summary>
Expand Down Expand Up @@ -431,6 +468,77 @@ public void WriteEndOfCentralDirectory(long noOfEntries, long sizeEntries,
}
}

/// <summary>
/// Write the required records to end the central directory.
/// </summary>
/// <param name="noOfEntries">The number of entries in the directory.</param>
/// <param name="sizeEntries">The size of the entries in the directory.</param>
/// <param name="startOfCentralDirectory">The start of the central directory.</param>
/// <param name="comment">The archive comment. (This can be null).</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
public async Task WriteEndOfCentralDirectoryAsync(long noOfEntries, long sizeEntries,
long startOfCentralDirectory, byte[] comment, CancellationToken cancellationToken)
{
if ((noOfEntries >= 0xffff) ||
(startOfCentralDirectory >= 0xffffffff) ||
(sizeEntries >= 0xffffffff))
{
await WriteZip64EndOfCentralDirectoryAsync(noOfEntries, sizeEntries, startOfCentralDirectory, cancellationToken);
}

await WriteLEIntAsync(ZipConstants.EndOfCentralDirectorySignature, cancellationToken);

// TODO: ZipFile Multi disk handling not done
await WriteLEShortAsync(0, cancellationToken); // number of this disk
await WriteLEShortAsync(0, cancellationToken); // no of disk with start of central dir

// Number of entries
if (noOfEntries >= 0xffff)
{
await WriteLEUshortAsync(0xffff, cancellationToken); // Zip64 marker
await WriteLEUshortAsync(0xffff, cancellationToken);
}
else
{
await WriteLEShortAsync((short)noOfEntries, cancellationToken); // entries in central dir for this disk
await WriteLEShortAsync((short)noOfEntries, cancellationToken); // total entries in central directory
}

// Size of the central directory
if (sizeEntries >= 0xffffffff)
{
await WriteLEUintAsync(0xffffffff, cancellationToken); // Zip64 marker
}
else
{
await WriteLEIntAsync((int)sizeEntries, cancellationToken);
}

// offset of start of central directory
if (startOfCentralDirectory >= 0xffffffff)
{
await WriteLEUintAsync(0xffffffff, cancellationToken); // Zip64 marker
}
else
{
await WriteLEIntAsync((int)startOfCentralDirectory, cancellationToken);
}

int commentLength = (comment != null) ? comment.Length : 0;

if (commentLength > 0xffff)
{
throw new ZipException(string.Format("Comment length({0}) is too long can only be 64K", commentLength));
}

await WriteLEShortAsync(commentLength, cancellationToken);

if (commentLength > 0)
{
await WriteAsync(comment, 0, comment.Length, cancellationToken);
}
}

#region LE value reading/writing

/// <summary>
Expand Down Expand Up @@ -495,6 +603,16 @@ public void WriteLEShort(int value)
stream_.WriteByte((byte)((value >> 8) & 0xff));
}

/// <summary>
/// Write an unsigned short in little endian byte order.
/// </summary>
/// <param name="value">The value to write.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
public async Task WriteLEShortAsync(int value, CancellationToken cancellationToken)
{
await stream_.WriteAsync(new[] { (byte)(value & 0xff), (byte)((value >> 8) & 0xff) }, 0, 2, cancellationToken);
}

/// <summary>
/// Write a ushort in little endian byte order.
/// </summary>
Expand All @@ -505,6 +623,16 @@ public void WriteLEUshort(ushort value)
stream_.WriteByte((byte)(value >> 8));
}

/// <summary>
/// Write a ushort in little endian byte order.
/// </summary>
/// <param name="value">The value to write.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
public async Task WriteLEUshortAsync(ushort value, CancellationToken cancellationToken)
{
await stream_.WriteAsync(new[] { (byte)(value & 0xff), (byte)(value >> 8) }, 0, 2, cancellationToken);
}

/// <summary>
/// Write an int in little endian byte order.
/// </summary>
Expand All @@ -515,6 +643,17 @@ public void WriteLEInt(int value)
WriteLEShort(value >> 16);
}

/// <summary>
/// Write an int in little endian byte order.
/// </summary>
/// <param name="value">The value to write.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
public async Task WriteLEIntAsync(int value, CancellationToken cancellationToken)
{
await WriteLEShortAsync(value, cancellationToken);
await WriteLEShortAsync(value >> 16, cancellationToken);
}

/// <summary>
/// Write a uint in little endian byte order.
/// </summary>
Expand All @@ -525,6 +664,17 @@ public void WriteLEUint(uint value)
WriteLEUshort((ushort)(value >> 16));
}

/// <summary>
/// Write a uint in little endian byte order.
/// </summary>
/// <param name="value">The value to write.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
public async Task WriteLEUintAsync(uint value, CancellationToken cancellationToken)
{
await WriteLEUshortAsync((ushort)(value & 0xffff), cancellationToken);
await WriteLEUshortAsync((ushort)(value >> 16), cancellationToken);
}

/// <summary>
/// Write a long in little endian byte order.
/// </summary>
Expand All @@ -535,6 +685,17 @@ public void WriteLELong(long value)
WriteLEInt((int)(value >> 32));
}

/// <summary>
/// Write a long in little endian byte order.
/// </summary>
/// <param name="value">The value to write.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
public async Task WriteLELongAsync(long value, CancellationToken cancellationToken)
{
await WriteLEIntAsync((int)value, cancellationToken);
await WriteLEIntAsync((int)(value >> 32), cancellationToken);
}

/// <summary>
/// Write a ulong in little endian byte order.
/// </summary>
Expand All @@ -545,6 +706,17 @@ public void WriteLEUlong(ulong value)
WriteLEUint((uint)(value >> 32));
}

/// <summary>
/// Write a ulong in little endian byte order.
/// </summary>
/// <param name="value">The value to write.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
public async Task WriteLEUlongAsync(ulong value, CancellationToken cancellationToken)
{
await WriteLEUintAsync((uint)(value & 0xffffffff), cancellationToken);
await WriteLEUintAsync((uint)(value >> 32), cancellationToken);
}

#endregion LE value reading/writing

/// <summary>
Expand Down
Loading