diff --git a/src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs b/src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs
index 0b1a647fe..456ec928e 100644
--- a/src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs
@@ -3,7 +3,9 @@
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
using System.IO;
-using System.Text;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
namespace ICSharpCode.SharpZipLib.GZip
{
@@ -184,6 +186,30 @@ protected override void Dispose(bool disposing)
}
}
}
+
+#if NETSTANDARD2_1_OR_GREATER
+ ///
+ public override async ValueTask DisposeAsync()
+ {
+ try
+ {
+ await FinishAsync(CancellationToken.None);
+ }
+ finally
+ {
+ if (state_ != OutputState.Closed)
+ {
+ state_ = OutputState.Closed;
+ if (IsStreamOwner)
+ {
+ await baseOutputStream_.DisposeAsync();
+ }
+ }
+
+ await base.DisposeAsync();
+ }
+ }
+#endif
///
/// Flushes the stream by ensuring the header is written, and then calling Flush
@@ -218,74 +244,119 @@ public override void Finish()
{
state_ = OutputState.Finished;
base.Finish();
-
- var totalin = (uint)(deflater_.TotalIn & 0xffffffff);
- var crcval = (uint)(crc.Value & 0xffffffff);
-
- byte[] gzipFooter;
-
- unchecked
- {
- gzipFooter = new byte[] {
- (byte) crcval, (byte) (crcval >> 8),
- (byte) (crcval >> 16), (byte) (crcval >> 24),
-
- (byte) totalin, (byte) (totalin >> 8),
- (byte) (totalin >> 16), (byte) (totalin >> 24)
- };
- }
-
+ var gzipFooter = GetFooter();
baseOutputStream_.Write(gzipFooter, 0, gzipFooter.Length);
}
}
+
+ ///
+ public override async Task FlushAsync(CancellationToken ct)
+ {
+ await WriteHeaderAsync();
+ await base.FlushAsync(ct);
+ }
+
+
+ ///
+ public override async Task FinishAsync(CancellationToken ct)
+ {
+ // If no data has been written a header should be added.
+ if (state_ == OutputState.Header)
+ {
+ await WriteHeaderAsync();
+ }
+
+ if (state_ == OutputState.Footer)
+ {
+ state_ = OutputState.Finished;
+ await base.FinishAsync(ct);
+ var gzipFooter = GetFooter();
+ await baseOutputStream_.WriteAsync(gzipFooter, 0, gzipFooter.Length, ct);
+ }
+ }
#endregion DeflaterOutputStream overrides
#region Support Routines
- private static string CleanFilename(string path)
- => path.Substring(path.LastIndexOf('/') + 1);
-
- private void WriteHeader()
+ private byte[] GetFooter()
{
- if (state_ == OutputState.Header)
- {
- state_ = OutputState.Footer;
+ var totalin = (uint)(deflater_.TotalIn & 0xffffffff);
+ var crcval = (uint)(crc.Value & 0xffffffff);
- var mod_time = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000L); // Ticks give back 100ns intervals
- byte[] gzipHeader = {
- // The two magic bytes
- GZipConstants.ID1,
- GZipConstants.ID2,
+ byte[] gzipFooter;
- // The compression type
- GZipConstants.CompressionMethodDeflate,
+ unchecked
+ {
+ gzipFooter = new [] {
+ (byte) crcval,
+ (byte) (crcval >> 8),
+ (byte) (crcval >> 16),
+ (byte) (crcval >> 24),
+ (byte) totalin,
+ (byte) (totalin >> 8),
+ (byte) (totalin >> 16),
+ (byte) (totalin >> 24),
+ };
+ }
- // The flags (not set)
- (byte)flags,
+ return gzipFooter;
+ }
- // The modification time
- (byte) mod_time, (byte) (mod_time >> 8),
- (byte) (mod_time >> 16), (byte) (mod_time >> 24),
+ private byte[] GetHeader()
+ {
+ var modTime = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000L); // Ticks give back 100ns intervals
+ byte[] gzipHeader = {
+ // The two magic bytes
+ GZipConstants.ID1,
+ GZipConstants.ID2,
- // The extra flags
- 0,
+ // The compression type
+ GZipConstants.CompressionMethodDeflate,
- // The OS type (unknown)
- 255
- };
+ // The flags (not set)
+ (byte)flags,
- baseOutputStream_.Write(gzipHeader, 0, gzipHeader.Length);
+ // The modification time
+ (byte) modTime, (byte) (modTime >> 8),
+ (byte) (modTime >> 16), (byte) (modTime >> 24),
- if (flags.HasFlag(GZipFlags.FNAME))
- {
- var fname = GZipConstants.Encoding.GetBytes(fileName);
- baseOutputStream_.Write(fname, 0, fname.Length);
+ // The extra flags
+ 0,
- // End filename string with a \0
- baseOutputStream_.Write(new byte[] { 0 }, 0, 1);
- }
+ // The OS type (unknown)
+ 255
+ };
+
+ if (!flags.HasFlag(GZipFlags.FNAME))
+ {
+ return gzipHeader;
}
+
+
+ return gzipHeader
+ .Concat(GZipConstants.Encoding.GetBytes(fileName))
+ .Concat(new byte []{0}) // End filename string with a \0
+ .ToArray();
+ }
+
+ private static string CleanFilename(string path)
+ => path.Substring(path.LastIndexOf('/') + 1);
+
+ private void WriteHeader()
+ {
+ if (state_ != OutputState.Header) return;
+ state_ = OutputState.Footer;
+ var gzipHeader = GetHeader();
+ baseOutputStream_.Write(gzipHeader, 0, gzipHeader.Length);
+ }
+
+ private async Task WriteHeaderAsync()
+ {
+ if (state_ != OutputState.Header) return;
+ state_ = OutputState.Footer;
+ var gzipHeader = GetHeader();
+ await baseOutputStream_.WriteAsync(gzipHeader, 0, gzipHeader.Length);
}
#endregion Support Routines
diff --git a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs
index 1c54b6848..a9b78dd75 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs
@@ -412,7 +412,7 @@ protected override void Dispose(bool disposing)
}
}
-#if NETSTANDARD2_1
+#if NETSTANDARD2_1_OR_GREATER
///
/// Calls and closes the underlying
/// stream when is true.
diff --git a/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipAsyncTests.cs b/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipAsyncTests.cs
new file mode 100644
index 000000000..209ae15d4
--- /dev/null
+++ b/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipAsyncTests.cs
@@ -0,0 +1,144 @@
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using ICSharpCode.SharpZipLib.GZip;
+using ICSharpCode.SharpZipLib.Tests.TestSupport;
+using NUnit.Framework;
+
+namespace ICSharpCode.SharpZipLib.Tests.GZip
+{
+
+
+ [TestFixture]
+ public class GZipAsyncTests
+ {
+ [Test]
+ [Category("GZip")]
+ [Category("Async")]
+ public async Task SmallBufferDecompressionAsync([Values(0, 1, 3)] int seed)
+ {
+ var outputBufferSize = 100000;
+ var outputBuffer = new byte[outputBufferSize];
+ var inputBuffer = Utils.GetDummyBytes(outputBufferSize * 4, seed);
+
+#if NETCOREAPP3_1_OR_GREATER
+ await using var msGzip = new MemoryStream();
+ await using (var gzos = new GZipOutputStream(msGzip){IsStreamOwner = false})
+ {
+ await gzos.WriteAsync(inputBuffer, 0, inputBuffer.Length);
+ }
+
+ msGzip.Seek(0, SeekOrigin.Begin);
+
+ using (var gzis = new GZipInputStream(msGzip))
+ await using (var msRaw = new MemoryStream())
+ {
+ int readOut;
+ while ((readOut = gzis.Read(outputBuffer, 0, outputBuffer.Length)) > 0)
+ {
+ await msRaw.WriteAsync(outputBuffer, 0, readOut);
+ }
+
+ var resultBuffer = msRaw.ToArray();
+ for (var i = 0; i < resultBuffer.Length; i++)
+ {
+ Assert.AreEqual(inputBuffer[i], resultBuffer[i]);
+ }
+ }
+#else
+ using var msGzip = new MemoryStream();
+ using (var gzos = new GZipOutputStream(msGzip){IsStreamOwner = false})
+ {
+ await gzos.WriteAsync(inputBuffer, 0, inputBuffer.Length);
+ }
+
+ msGzip.Seek(0, SeekOrigin.Begin);
+
+ using (var gzis = new GZipInputStream(msGzip))
+ using (var msRaw = new MemoryStream())
+ {
+ int readOut;
+ while ((readOut = gzis.Read(outputBuffer, 0, outputBuffer.Length)) > 0)
+ {
+ await msRaw.WriteAsync(outputBuffer, 0, readOut);
+ }
+
+ var resultBuffer = msRaw.ToArray();
+ for (var i = 0; i < resultBuffer.Length; i++)
+ {
+ Assert.AreEqual(inputBuffer[i], resultBuffer[i]);
+ }
+ }
+#endif
+ }
+
+ ///
+ /// Basic compress/decompress test
+ ///
+ [Test]
+ [Category("GZip")]
+ [Category("Async")]
+ public async Task OriginalFilenameAsync()
+ {
+ var content = "FileContents";
+
+#if NETCOREAPP3_1_OR_GREATER
+ await using var ms = new MemoryStream();
+ await using (var outStream = new GZipOutputStream(ms) { IsStreamOwner = false })
+ {
+ outStream.FileName = "/path/to/file.ext";
+ outStream.Write(Encoding.ASCII.GetBytes(content));
+ }
+#else
+ var ms = new MemoryStream();
+ var outStream = new GZipOutputStream(ms){ IsStreamOwner = false };
+ outStream.FileName = "/path/to/file.ext";
+ var bytes = Encoding.ASCII.GetBytes(content);
+ outStream.Write(bytes, 0, bytes.Length);
+ await outStream.FinishAsync(System.Threading.CancellationToken.None);
+ outStream.Dispose();
+
+#endif
+ ms.Seek(0, SeekOrigin.Begin);
+
+ using (var inStream = new GZipInputStream(ms))
+ {
+ var readBuffer = new byte[content.Length];
+ inStream.Read(readBuffer, 0, readBuffer.Length);
+ Assert.AreEqual(content, Encoding.ASCII.GetString(readBuffer));
+ Assert.AreEqual("file.ext", inStream.GetFilename());
+ }
+ }
+
+ ///
+ /// Test creating an empty gzip stream using async
+ ///
+ [Test]
+ [Category("GZip")]
+ [Category("Async")]
+ public async Task EmptyGZipStreamAsync()
+ {
+#if NETCOREAPP3_1_OR_GREATER
+ await using var ms = new MemoryStream();
+ await using (var outStream = new GZipOutputStream(ms) { IsStreamOwner = false })
+ {
+ // No content
+ }
+#else
+ var ms = new MemoryStream();
+ var outStream = new GZipOutputStream(ms){ IsStreamOwner = false };
+ await outStream.FinishAsync(System.Threading.CancellationToken.None);
+ outStream.Dispose();
+
+#endif
+ ms.Seek(0, SeekOrigin.Begin);
+
+ using (var inStream = new GZipInputStream(ms))
+ using (var reader = new StreamReader(inStream))
+ {
+ var content = await reader.ReadToEndAsync();
+ Assert.IsEmpty(content);
+ }
+ }
+ }
+}
diff --git a/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipTests.cs b/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipTests.cs
index 62be609fc..3241fd134 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipTests.cs
+++ b/test/ICSharpCode.SharpZipLib.Tests/GZip/GZipTests.cs
@@ -4,6 +4,8 @@
using System;
using System.IO;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
namespace ICSharpCode.SharpZipLib.Tests.GZip
{
@@ -386,32 +388,23 @@ public void FlushToUnderlyingStream()
[Test]
[Category("GZip")]
- public void SmallBufferDecompression()
+ public void SmallBufferDecompression([Values(0, 1, 3)] int seed)
{
var outputBufferSize = 100000;
- var inputBufferSize = outputBufferSize * 4;
- var inputBuffer = Utils.GetDummyBytes(inputBufferSize, seed: 0);
-
var outputBuffer = new byte[outputBufferSize];
+ var inputBuffer = Utils.GetDummyBytes(outputBufferSize * 4, seed);
using var msGzip = new MemoryStream();
- using (var gzos = new GZipOutputStream(msGzip))
+ using (var gzos = new GZipOutputStream(msGzip){IsStreamOwner = false})
{
- gzos.IsStreamOwner = false;
-
gzos.Write(inputBuffer, 0, inputBuffer.Length);
-
- gzos.Flush();
- gzos.Finish();
}
msGzip.Seek(0, SeekOrigin.Begin);
-
-
+
using (var gzis = new GZipInputStream(msGzip))
using (var msRaw = new MemoryStream())
{
-
int readOut;
while ((readOut = gzis.Read(outputBuffer, 0, outputBuffer.Length)) > 0)
{
@@ -419,13 +412,10 @@ public void SmallBufferDecompression()
}
var resultBuffer = msRaw.ToArray();
-
for (var i = 0; i < resultBuffer.Length; i++)
{
Assert.AreEqual(inputBuffer[i], resultBuffer[i]);
}
-
-
}
}
diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStreamAsyncTests.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStreamAsyncTests.cs
index 9a8aeac14..aff027bf1 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStreamAsyncTests.cs
+++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStreamAsyncTests.cs
@@ -1,8 +1,8 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using ICSharpCode.SharpZipLib.Tests.TestSupport;
using ICSharpCode.SharpZipLib.Zip;
+using ICSharpCode.SharpZipLib.Tests.TestSupport;
using NUnit.Framework;
namespace ICSharpCode.SharpZipLib.Tests.Zip
@@ -10,12 +10,12 @@ namespace ICSharpCode.SharpZipLib.Tests.Zip
[TestFixture]
public class ZipStreamAsyncTests
{
-#if NETCOREAPP3_1_OR_GREATER
[Test]
[Category("Zip")]
[Category("Async")]
public async Task WriteZipStreamUsingAsync()
{
+#if NETCOREAPP3_1_OR_GREATER
await using var ms = new MemoryStream();
await using (var outStream = new ZipOutputStream(ms){IsStreamOwner = false})
@@ -28,8 +28,11 @@ public async Task WriteZipStreamUsingAsync()
}
ZipTesting.AssertValidZip(ms);
- }
+#else
+ await Task.CompletedTask;
+ Assert.Ignore("Async Using is not supported");
#endif
+ }
[Test]
[Category("Zip")]
@@ -119,4 +122,4 @@ public async Task WriteReadOnlyZipStreamAsync ()
}
}
-}
+}
\ No newline at end of file