Skip to content
3 changes: 3 additions & 0 deletions src/ICSharpCode.SharpZipLib/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("ICSharpCode.SharpZipLib.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b9a14ea8fc9d7599e0e82a1292a23103f0210e2f928a0f466963af23fffadba59dcc8c9e26ecd114d7c0b4179e4bc93b1656b7ee2d4a67dd7c1992653e0d9cc534f7914b6f583b022e0a7aa8a430f407932f9a6806f0fc64d61e78d5ae01aa8f8233196719d44da2c50a2d1cfa3f7abb7487b3567a4f0456aa6667154c6749b1")]
130 changes: 130 additions & 0 deletions src/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using CT = System.Threading.CancellationToken;

// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable InconsistentNaming

namespace ICSharpCode.SharpZipLib.Core
{
internal static class ByteOrderStreamExtensions
{
internal static byte[] SwappedBytes(ushort value) => new[] {(byte)value, (byte)(value >> 8)};
internal static byte[] SwappedBytes(short value) => new[] {(byte)value, (byte)(value >> 8)};
internal static byte[] SwappedBytes(uint value) => new[] {(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24)};
internal static byte[] SwappedBytes(int value) => new[] {(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24)};

internal static byte[] SwappedBytes(long value) => new[] {
(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24),
(byte)(value >> 32), (byte)(value >> 40), (byte)(value >> 48), (byte)(value >> 56)
};

internal static byte[] SwappedBytes(ulong value) => new[] {
(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24),
(byte)(value >> 32), (byte)(value >> 40), (byte)(value >> 48), (byte)(value >> 56)
};

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static long SwappedS64(byte[] bytes) => (
(long)bytes[0] << 0 | (long)bytes[1] << 8 | (long)bytes[2] << 16 | (long)bytes[3] << 24 |
(long)bytes[4] << 32 | (long)bytes[5] << 40 | (long)bytes[6] << 48 | (long)bytes[7] << 56);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ulong SwappedU64(byte[] bytes) => (
(ulong)bytes[0] << 0 | (ulong)bytes[1] << 8 | (ulong)bytes[2] << 16 | (ulong)bytes[3] << 24 |
(ulong)bytes[4] << 32 | (ulong)bytes[5] << 40 | (ulong)bytes[6] << 48 | (ulong)bytes[7] << 56);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int SwappedS32(byte[] bytes) => bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static uint SwappedU32(byte[] bytes) => (uint) SwappedS32(bytes);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static short SwappedS16(byte[] bytes) => (short)(bytes[0] | bytes[1] << 8);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ushort SwappedU16(byte[] bytes) => (ushort) SwappedS16(bytes);

internal static byte[] ReadBytes(this Stream stream, int count)
{
var bytes = new byte[count];
var remaining = count;
while (remaining > 0)
{
var bytesRead = stream.Read(bytes, count - remaining, remaining);
if (bytesRead < 1) throw new EndOfStreamException();
remaining -= bytesRead;
}

return bytes;
}

/// <summary> Read an unsigned short in little endian byte order. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ReadLEShort(this Stream stream) => SwappedS16(ReadBytes(stream, 2));

/// <summary> Read an int in little endian byte order. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ReadLEInt(this Stream stream) => SwappedS32(ReadBytes(stream, 4));

/// <summary> Read a long in little endian byte order. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long ReadLELong(this Stream stream) => SwappedS64(ReadBytes(stream, 8));

/// <summary> Write an unsigned short in little endian byte order. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLEShort(this Stream stream, int value) => stream.Write(SwappedBytes(value), 0, 2);

/// <inheritdoc cref="WriteLEShort"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task WriteLEShortAsync(this Stream stream, int value, CT ct)
=> await stream.WriteAsync(SwappedBytes(value), 0, 2, ct);

/// <summary> Write a ushort in little endian byte order. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLEUshort(this Stream stream, ushort value) => stream.Write(SwappedBytes(value), 0, 2);

/// <inheritdoc cref="WriteLEUshort"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task WriteLEUshortAsync(this Stream stream, ushort value, CT ct)
=> await stream.WriteAsync(SwappedBytes(value), 0, 2, ct);

/// <summary> Write an int in little endian byte order. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLEInt(this Stream stream, int value) => stream.Write(SwappedBytes(value), 0, 4);

/// <inheritdoc cref="WriteLEInt"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task WriteLEIntAsync(this Stream stream, int value, CT ct)
=> await stream.WriteAsync(SwappedBytes(value), 0, 4, ct);

/// <summary> Write a uint in little endian byte order. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLEUint(this Stream stream, uint value) => stream.Write(SwappedBytes(value), 0, 4);

/// <inheritdoc cref="WriteLEUint"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task WriteLEUintAsync(this Stream stream, uint value, CT ct)
=> await stream.WriteAsync(SwappedBytes(value), 0, 4, ct);

/// <summary> Write a long in little endian byte order. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLELong(this Stream stream, long value) => stream.Write(SwappedBytes(value), 0, 8);

/// <inheritdoc cref="WriteLELong"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task WriteLELongAsync(this Stream stream, long value, CT ct)
=> await stream.WriteAsync(SwappedBytes(value), 0, 8, ct);

/// <summary> Write a ulong in little endian byte order. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLEUlong(this Stream stream, ulong value) => stream.Write(SwappedBytes(value), 0, 8);

/// <inheritdoc cref="WriteLEUlong"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task WriteLEUlongAsync(this Stream stream, ulong value, CT ct)
=> await stream.WriteAsync(SwappedBytes(value), 0, 8, ct);
}
}
37 changes: 24 additions & 13 deletions src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace ICSharpCode.SharpZipLib.Core
{
/// <summary>
/// Provides simple <see cref="Stream"/>" utilities.
/// </summary>
public sealed class StreamUtils
public static class StreamUtils
{
/// <summary>
/// Read from a <see cref="Stream"/> ensuring all the required data is read.
/// </summary>
/// <param name="stream">The stream to read.</param>
/// <param name="buffer">The buffer to fill.</param>
/// <seealso cref="ReadFully(Stream,byte[],int,int)"/>
static public void ReadFully(Stream stream, byte[] buffer)
public static void ReadFully(Stream stream, byte[] buffer)
{
ReadFully(stream, buffer, 0, buffer.Length);
}
Expand All @@ -29,7 +31,7 @@ static public void ReadFully(Stream stream, byte[] buffer)
/// <exception cref="ArgumentNullException">Required parameter is null</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> and or <paramref name="count"/> are invalid.</exception>
/// <exception cref="EndOfStreamException">End of stream is encountered before all the data has been read.</exception>
static public void ReadFully(Stream stream, byte[] buffer, int offset, int count)
public static void ReadFully(Stream stream, byte[] buffer, int offset, int count)
{
if (stream == null)
{
Expand Down Expand Up @@ -73,7 +75,7 @@ static public void ReadFully(Stream stream, byte[] buffer, int offset, int count
/// <param name="count">The number of bytes of data to store.</param>
/// <exception cref="ArgumentNullException">Required parameter is null</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> and or <paramref name="count"/> are invalid.</exception>
static public int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count)
public static int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count)
{
if (stream == null)
{
Expand Down Expand Up @@ -118,7 +120,7 @@ static public int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, i
/// <param name="source">The stream to source data from.</param>
/// <param name="destination">The stream to write data to.</param>
/// <param name="buffer">The buffer to use during copying.</param>
static public void Copy(Stream source, Stream destination, byte[] buffer)
public static void Copy(Stream source, Stream destination, byte[] buffer)
{
if (source == null)
{
Expand Down Expand Up @@ -169,7 +171,7 @@ static public void Copy(Stream source, Stream destination, byte[] buffer)
/// <param name="sender">The source for this event.</param>
/// <param name="name">The name to use with the event.</param>
/// <remarks>This form is specialised for use within #Zip to support events during archive operations.</remarks>
static public void Copy(Stream source, Stream destination,
public static void Copy(Stream source, Stream destination,
byte[] buffer, ProgressHandler progressHandler, TimeSpan updateInterval, object sender, string name)
{
Copy(source, destination, buffer, progressHandler, updateInterval, sender, name, -1);
Expand All @@ -188,7 +190,7 @@ static public void Copy(Stream source, Stream destination,
/// <param name="fixedTarget">A predetermined fixed target value to use with progress updates.
/// If the value is negative the target is calculated by looking at the stream.</param>
/// <remarks>This form is specialised for use within #Zip to support events during archive operations.</remarks>
static public void Copy(Stream source, Stream destination,
public static void Copy(Stream source, Stream destination,
byte[] buffer,
ProgressHandler progressHandler, TimeSpan updateInterval,
object sender, string name, long fixedTarget)
Expand Down Expand Up @@ -272,13 +274,22 @@ static public void Copy(Stream source, Stream destination,
progressHandler(sender, args);
}
}

/// <summary>
/// Initialise an instance of <see cref="StreamUtils"></see>
/// </summary>
private StreamUtils()

internal static async Task WriteProcToStreamAsync(this Stream targetStream, MemoryStream bufferStream, Action<Stream> writeProc, CancellationToken ct)
{
// Do nothing.
bufferStream.SetLength(0);
writeProc(bufferStream);
bufferStream.Position = 0;
await bufferStream.CopyToAsync(targetStream, 81920, ct);
bufferStream.SetLength(0);
}

internal static async Task WriteProcToStreamAsync(this Stream targetStream, Action<Stream> writeProc, CancellationToken ct)
{
using (var ms = new MemoryStream())
{
await WriteProcToStreamAsync(targetStream, ms, writeProc, ct);
}
}
}
}
2 changes: 1 addition & 1 deletion src/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ Please see https://github.com/icsharpcode/SharpZipLib/wiki/Release-1.3.3 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 @@ -105,10 +107,7 @@ public virtual void Finish()
break;
}

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

baseOutputStream_.Write(buffer_, 0, len);
}
Expand All @@ -131,6 +130,47 @@ public virtual void Finish()
}
}

/// <summary>
/// Finishes the stream by calling finish() on the deflater.
/// </summary>
/// <param name="ct">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 ct)
{
deflater_.Finish();
while (!deflater_.IsFinished)
{
int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
if (len <= 0)
{
break;
}

EncryptBlock(buffer_, 0, len);

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

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

await baseOutputStream_.FlushAsync(ct);

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 @@ -177,6 +217,7 @@ public bool CanPatchEntries
/// </param>
protected void EncryptBlock(byte[] buffer, int offset, int length)
{
if(cryptoTransform_ is null) return;
cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
}

Expand Down Expand Up @@ -204,10 +245,8 @@ private void Deflate(bool flushing)
{
break;
}
if (cryptoTransform_ != null)
{
EncryptBlock(buffer_, 0, deflateCount);
}

EncryptBlock(buffer_, 0, deflateCount);

baseOutputStream_.Write(buffer_, 0, deflateCount);
}
Expand Down Expand Up @@ -369,6 +408,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
Loading