From 2b4f02997ede997f94bdc74ca465e1dc33963d7f Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 1 Feb 2021 08:34:12 +0000 Subject: [PATCH] more async await --- .../Archives/AbstractWritableArchive.cs | 9 +- .../Archives/GZip/GZipArchive.cs | 36 +- .../Archives/IWritableArchive.cs | 3 +- .../Archives/IWritableArchiveExtensions.cs | 13 +- src/SharpCompress/Archives/Tar/TarArchive.cs | 30 +- src/SharpCompress/Archives/Zip/ZipArchive.cs | 19 +- src/SharpCompress/Common/ExtractionMethods.cs | 18 +- src/SharpCompress/Common/GZip/GZipEntry.cs | 4 +- .../Common/Zip/StreamingZipHeaderFactory.cs | 7 +- src/SharpCompress/Readers/AbstractReader.cs | 58 ++- src/SharpCompress/Readers/GZip/GZipReader.cs | 2 +- src/SharpCompress/Readers/IReader.cs | 10 +- .../Readers/IReaderExtensions.cs | 64 +-- src/SharpCompress/Readers/Zip/ZipReader.cs | 4 +- src/SharpCompress/SharpCompress.csproj | 24 ++ src/SharpCompress/Utility.cs | 22 +- src/SharpCompress/Writers/GZip/GZipWriter.cs | 18 +- .../Writers/IWriterExtensions.cs | 30 +- src/SharpCompress/Writers/Zip/ZipWriter.cs | 388 ++++++++++-------- .../GZip/GZipArchiveTests.cs | 7 +- .../GZip/GZipWriterTests.cs | 6 +- .../SharpCompress.Test.csproj | 15 + .../SharpCompress.Test/Tar/TarArchiveTests.cs | 16 +- tests/SharpCompress.Test/WriterTests.cs | 2 +- tests/SharpCompress.Test/Zip/Zip64Tests.cs | 40 +- .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 49 +-- .../SharpCompress.Test/Zip/ZipReaderTests.cs | 10 +- 27 files changed, 507 insertions(+), 397 deletions(-) diff --git a/src/SharpCompress/Archives/AbstractWritableArchive.cs b/src/SharpCompress/Archives/AbstractWritableArchive.cs index e81a9924..d69dd06b 100644 --- a/src/SharpCompress/Archives/AbstractWritableArchive.cs +++ b/src/SharpCompress/Archives/AbstractWritableArchive.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Readers; using SharpCompress.Writers; @@ -137,11 +139,11 @@ namespace SharpCompress.Archives return false; } - public void SaveTo(Stream stream, WriterOptions options) + public async Task SaveToAsync(Stream stream, WriterOptions options) { //reset streams of new entries newEntries.Cast().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin)); - SaveTo(stream, options, OldEntries, newEntries); + await SaveToAsync(stream, options, OldEntries, newEntries); } protected TEntry CreateEntry(string key, Stream source, long size, DateTime? modified, @@ -157,7 +159,8 @@ namespace SharpCompress.Archives protected abstract TEntry CreateEntryInternal(string key, Stream source, long size, DateTime? modified, bool closeStream); - protected abstract void SaveTo(Stream stream, WriterOptions options, IEnumerable oldEntries, IEnumerable newEntries); + protected abstract Task SaveToAsync(Stream stream, WriterOptions options, IEnumerable oldEntries, IEnumerable newEntries, + CancellationToken cancellationToken = default); public override void Dispose() { diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs index 025aacaa..b179259c 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchive.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.GZip; using SharpCompress.Readers; @@ -82,17 +84,15 @@ namespace SharpCompress.Archives.GZip return IsGZipFile(stream); } - public void SaveTo(string filePath) + public Task SaveToAsync(string filePath) { - SaveTo(new FileInfo(filePath)); + return SaveToAsync(new FileInfo(filePath)); } - public void SaveTo(FileInfo fileInfo) + public async Task SaveToAsync(FileInfo fileInfo) { - using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write)) - { - SaveTo(stream, new WriterOptions(CompressionType.GZip)); - } + using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write); + await SaveToAsync(stream, new WriterOptions(CompressionType.GZip)); } public static bool IsGZipFile(Stream stream) @@ -139,24 +139,22 @@ namespace SharpCompress.Archives.GZip return new GZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream); } - protected override void SaveTo(Stream stream, WriterOptions options, - IEnumerable oldEntries, - IEnumerable newEntries) + protected override async Task SaveToAsync(Stream stream, WriterOptions options, + IEnumerable oldEntries, + IEnumerable newEntries, + CancellationToken cancellationToken = default) { if (Entries.Count > 1) { throw new InvalidOperationException("Only one entry is allowed in a GZip Archive"); } - using (var writer = new GZipWriter(stream, new GZipWriterOptions(options))) + + await using var writer = new GZipWriter(stream, new GZipWriterOptions(options)); + foreach (var entry in oldEntries.Concat(newEntries) + .Where(x => !x.IsDirectory)) { - foreach (var entry in oldEntries.Concat(newEntries) - .Where(x => !x.IsDirectory)) - { - using (var entryStream = entry.OpenEntryStream()) - { - writer.Write(entry.Key, entryStream, entry.LastModifiedTime); - } - } + await using var entryStream = entry.OpenEntryStream(); + await writer.WriteAsync(entry.Key, entryStream, entry.LastModifiedTime, cancellationToken); } } diff --git a/src/SharpCompress/Archives/IWritableArchive.cs b/src/SharpCompress/Archives/IWritableArchive.cs index 3b1b0328..385ff2dc 100644 --- a/src/SharpCompress/Archives/IWritableArchive.cs +++ b/src/SharpCompress/Archives/IWritableArchive.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading.Tasks; using SharpCompress.Writers; namespace SharpCompress.Archives @@ -10,7 +11,7 @@ namespace SharpCompress.Archives IArchiveEntry AddEntry(string key, Stream source, bool closeStream, long size = 0, DateTime? modified = null); - void SaveTo(Stream stream, WriterOptions options); + Task SaveToAsync(Stream stream, WriterOptions options); /// /// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended. diff --git a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs index 468622a7..f37ce479 100644 --- a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading.Tasks; using SharpCompress.Writers; namespace SharpCompress.Archives @@ -18,17 +19,15 @@ namespace SharpCompress.Archives fileInfo.LastWriteTime); } - public static void SaveTo(this IWritableArchive writableArchive, string filePath, WriterOptions options) + public static Task SaveToAsync(this IWritableArchive writableArchive, string filePath, WriterOptions options) { - writableArchive.SaveTo(new FileInfo(filePath), options); + return writableArchive.SaveToAsync(new FileInfo(filePath), options); } - public static void SaveTo(this IWritableArchive writableArchive, FileInfo fileInfo, WriterOptions options) + public static async Task SaveToAsync(this IWritableArchive writableArchive, FileInfo fileInfo, WriterOptions options) { - using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write)) - { - writableArchive.SaveTo(stream, options); - } + await using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write); + await writableArchive.SaveToAsync(stream, options); } public static void AddAllFromDirectory( diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 73607f29..51697568 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.Tar; using SharpCompress.Common.Tar.Headers; @@ -59,10 +61,9 @@ namespace SharpCompress.Archives.Tar { return false; } - using (Stream stream = fileInfo.OpenRead()) - { - return IsTarFile(stream); - } + + using Stream stream = fileInfo.OpenRead(); + return IsTarFile(stream); } public static bool IsTarFile(Stream stream) @@ -170,20 +171,17 @@ namespace SharpCompress.Archives.Tar closeStream); } - protected override void SaveTo(Stream stream, WriterOptions options, - IEnumerable oldEntries, - IEnumerable newEntries) + protected override async Task SaveToAsync(Stream stream, WriterOptions options, + IEnumerable oldEntries, + IEnumerable newEntries, + CancellationToken cancellationToken = default) { - using (var writer = new TarWriter(stream, new TarWriterOptions(options))) + await using var writer = new TarWriter(stream, new TarWriterOptions(options)); + foreach (var entry in oldEntries.Concat(newEntries) + .Where(x => !x.IsDirectory)) { - foreach (var entry in oldEntries.Concat(newEntries) - .Where(x => !x.IsDirectory)) - { - using (var entryStream = entry.OpenEntryStream()) - { - writer.Write(entry.Key, entryStream, entry.LastModifiedTime, entry.Size); - } - } + await using var entryStream = entry.OpenEntryStream(); + await writer.WriteAsync(entry.Key, entryStream, entry.LastModifiedTime, cancellationToken); } } diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index 58d94ee0..c3362eff 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.Zip; using SharpCompress.Common.Zip.Headers; @@ -163,23 +165,24 @@ namespace SharpCompress.Archives.Zip } } - public void SaveTo(Stream stream) + public Task SaveToAsync(Stream stream) { - SaveTo(stream, new WriterOptions(CompressionType.Deflate)); + return SaveToAsync(stream, new WriterOptions(CompressionType.Deflate)); } - protected override void SaveTo(Stream stream, WriterOptions options, - IEnumerable oldEntries, - IEnumerable newEntries) + protected override async Task SaveToAsync(Stream stream, WriterOptions options, + IEnumerable oldEntries, + IEnumerable newEntries, + CancellationToken cancellationToken = default) { - using (var writer = new ZipWriter(stream, new ZipWriterOptions(options))) + await using (var writer = new ZipWriter(stream, new ZipWriterOptions(options))) { foreach (var entry in oldEntries.Concat(newEntries) .Where(x => !x.IsDirectory)) { - using (var entryStream = entry.OpenEntryStream()) + await using (var entryStream = entry.OpenEntryStream()) { - writer.Write(entry.Key, entryStream, entry.LastModifiedTime); + await writer.WriteAsync(entry.Key, entryStream, entry.LastModifiedTime, cancellationToken); } } } diff --git a/src/SharpCompress/Common/ExtractionMethods.cs b/src/SharpCompress/Common/ExtractionMethods.cs index 738e3f39..fa1a84b3 100644 --- a/src/SharpCompress/Common/ExtractionMethods.cs +++ b/src/SharpCompress/Common/ExtractionMethods.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace SharpCompress.Common { @@ -8,10 +10,11 @@ namespace SharpCompress.Common /// /// Extract to specific directory, retaining filename /// - public static void WriteEntryToDirectory(IEntry entry, + public static async ValueTask WriteEntryToDirectoryAsync(IEntry entry, string destinationDirectory, ExtractionOptions? options, - Action write) + Func write, + CancellationToken cancellationToken = default) { string destinationFileName; string file = Path.GetFileName(entry.Key); @@ -52,7 +55,7 @@ namespace SharpCompress.Common { throw new ExtractionException("Entry is trying to write a file outside of the destination directory."); } - write(destinationFileName, options); + await write(destinationFileName, options, cancellationToken); } else if (options.ExtractFullPath && !Directory.Exists(destinationFileName)) { @@ -60,11 +63,12 @@ namespace SharpCompress.Common } } - public static void WriteEntryToFile(IEntry entry, string destinationFileName, + public static async ValueTask WriteEntryToFileAsync(IEntry entry, string destinationFileName, ExtractionOptions? options, - Action openAndWrite) + Func openAndWrite, + CancellationToken cancellationToken = default) { - if (entry.LinkTarget != null) + if (entry.LinkTarget is not null) { if (options?.WriteSymbolicLink is null) { @@ -85,7 +89,7 @@ namespace SharpCompress.Common fm = FileMode.CreateNew; } - openAndWrite(destinationFileName, fm); + await openAndWrite(destinationFileName, fm, cancellationToken); entry.PreserveExtractionOptions(destinationFileName, options); } } diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index 1204cfd7..c4f62000 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; namespace SharpCompress.Common.GZip { @@ -41,8 +42,9 @@ namespace SharpCompress.Common.GZip internal override IEnumerable Parts => _filePart.AsEnumerable(); - internal static IEnumerable GetEntries(Stream stream, OptionsBase options) + internal static async IAsyncEnumerable GetEntries(Stream stream, OptionsBase options) { + await Task.CompletedTask; yield return new GZipEntry(new GZipFilePart(stream, options.ArchiveEncoding)); } } diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs index 3c273e32..fd56c6a4 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using SharpCompress.Common.Zip.Headers; using SharpCompress.IO; @@ -12,8 +13,10 @@ namespace SharpCompress.Common.Zip { } - internal IEnumerable ReadStreamHeader(Stream stream) + internal async IAsyncEnumerable ReadStreamHeader(Stream stream) { + //TODO async stream reader? + await Task.CompletedTask; RewindableStream rewindableStream; if (stream is RewindableStream rs) @@ -27,7 +30,7 @@ namespace SharpCompress.Common.Zip while (true) { ZipHeader? header; - BinaryReader reader = new BinaryReader(rewindableStream); + BinaryReader reader = new(rewindableStream); if (_lastEntryHeader != null && (FlagUtility.HasFlag(_lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor) || _lastEntryHeader.IsZip64)) { diff --git a/src/SharpCompress/Readers/AbstractReader.cs b/src/SharpCompress/Readers/AbstractReader.cs index dfd012a3..99dc7f8d 100644 --- a/src/SharpCompress/Readers/AbstractReader.cs +++ b/src/SharpCompress/Readers/AbstractReader.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; namespace SharpCompress.Readers @@ -14,7 +16,7 @@ namespace SharpCompress.Readers where TVolume : Volume { private bool completed; - private IEnumerator? entriesForCurrentReadStream; + private IAsyncEnumerator? entriesForCurrentReadStream; private bool wroteCurrentEntry; public event EventHandler>? EntryExtractionProgress; @@ -40,13 +42,13 @@ namespace SharpCompress.Readers /// /// Current file entry /// - public TEntry Entry => entriesForCurrentReadStream!.Current; + public TEntry? Entry => entriesForCurrentReadStream?.Current ?? default; #region IDisposable Members - public void Dispose() + public async ValueTask DisposeAsync() { - entriesForCurrentReadStream?.Dispose(); + await (entriesForCurrentReadStream?.DisposeAsync() ?? new ValueTask(Task.CompletedTask)); Volume?.Dispose(); } @@ -67,7 +69,7 @@ namespace SharpCompress.Readers } } - public bool MoveToNextEntry() + public async ValueTask MoveToNextEntry() { if (completed) { @@ -79,14 +81,14 @@ namespace SharpCompress.Readers } if (entriesForCurrentReadStream is null) { - return LoadStreamForReading(RequestInitialStream()); + return await LoadStreamForReading(RequestInitialStream()); } if (!wroteCurrentEntry) { SkipEntry(); } wroteCurrentEntry = false; - if (NextEntryForCurrentStream()) + if (await NextEntryForCurrentStream()) { return true; } @@ -94,17 +96,17 @@ namespace SharpCompress.Readers return false; } - protected bool LoadStreamForReading(Stream stream) + protected async Task LoadStreamForReading(Stream stream) { - entriesForCurrentReadStream?.Dispose(); - if ((stream is null) || (!stream.CanRead)) + await (entriesForCurrentReadStream?.DisposeAsync() ?? new ValueTask(Task.CompletedTask)); + if (stream is null || !stream.CanRead) { throw new MultipartStreamRequiredException("File is split into multiple archives: '" - + Entry.Key + + + (Entry?.Key ?? "unknown") + "'. A new readable stream is required. Use Cancel if it was intended."); } - entriesForCurrentReadStream = GetEntries(stream).GetEnumerator(); - return entriesForCurrentReadStream.MoveNext(); + entriesForCurrentReadStream = GetEntries(stream).GetAsyncEnumerator(); + return await (entriesForCurrentReadStream?.MoveNextAsync() ?? new ValueTask(Task.FromResult(false))); } protected virtual Stream RequestInitialStream() @@ -112,18 +114,18 @@ namespace SharpCompress.Readers return Volume.Stream; } - internal virtual bool NextEntryForCurrentStream() + internal virtual async ValueTask NextEntryForCurrentStream() { - return entriesForCurrentReadStream!.MoveNext(); + return await (entriesForCurrentReadStream?.MoveNextAsync() ?? new ValueTask(Task.FromResult(false))); } - protected abstract IEnumerable GetEntries(Stream stream); + protected abstract IAsyncEnumerable GetEntries(Stream stream); #region Entry Skip/Write private void SkipEntry() { - if (!Entry.IsDirectory) + if (Entry?.IsDirectory == true) { Skip(); } @@ -131,6 +133,10 @@ namespace SharpCompress.Readers private void Skip() { + if (Entry is null) + { + return; + } if (ArchiveType != ArchiveType.Rar && !Entry.IsSolid && Entry.CompressedSize > 0) @@ -154,7 +160,7 @@ namespace SharpCompress.Readers } } - public void WriteEntryTo(Stream writableStream) + public async ValueTask WriteEntryToAsync(Stream writableStream, CancellationToken cancellationToken = default) { if (wroteCurrentEntry) { @@ -165,16 +171,20 @@ namespace SharpCompress.Readers throw new ArgumentNullException("A writable Stream was required. Use Cancel if that was intended."); } - Write(writableStream); + await WriteAsync(writableStream, cancellationToken); wroteCurrentEntry = true; } - internal void Write(Stream writeStream) + internal async ValueTask WriteAsync(Stream writeStream, CancellationToken cancellationToken) { + if (Entry is null) + { + throw new ArgumentException("Entry is null"); + } var streamListener = this as IReaderExtractionListener; using (Stream s = OpenEntryStream()) { - s.TransferTo(writeStream, Entry, streamListener); + await s.TransferToAsync(writeStream, Entry, streamListener, cancellationToken); } } @@ -199,12 +209,16 @@ namespace SharpCompress.Readers protected virtual EntryStream GetEntryStream() { + if (Entry is null) + { + throw new ArgumentException("Entry is null"); + } return CreateEntryStream(Entry.Parts.First().GetCompressedStream()); } #endregion - IEntry IReader.Entry => Entry; + IEntry? IReader.Entry => Entry; void IExtractionListener.FireCompressedBytesRead(long currentPartCompressedBytes, long compressedReadBytes) { diff --git a/src/SharpCompress/Readers/GZip/GZipReader.cs b/src/SharpCompress/Readers/GZip/GZipReader.cs index f4b889d0..a66c0ea5 100644 --- a/src/SharpCompress/Readers/GZip/GZipReader.cs +++ b/src/SharpCompress/Readers/GZip/GZipReader.cs @@ -31,7 +31,7 @@ namespace SharpCompress.Readers.GZip #endregion Open - protected override IEnumerable GetEntries(Stream stream) + protected override IAsyncEnumerable GetEntries(Stream stream) { return GZipEntry.GetEntries(stream, Options); } diff --git a/src/SharpCompress/Readers/IReader.cs b/src/SharpCompress/Readers/IReader.cs index db11164d..880c46ea 100644 --- a/src/SharpCompress/Readers/IReader.cs +++ b/src/SharpCompress/Readers/IReader.cs @@ -1,10 +1,12 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; namespace SharpCompress.Readers { - public interface IReader : IDisposable + public interface IReader : IAsyncDisposable { event EventHandler> EntryExtractionProgress; @@ -13,13 +15,13 @@ namespace SharpCompress.Readers ArchiveType ArchiveType { get; } - IEntry Entry { get; } + IEntry? Entry { get; } /// /// Decompresses the current entry to the stream. This cannot be called twice for the current entry. /// /// - void WriteEntryTo(Stream writableStream); + ValueTask WriteEntryToAsync(Stream writableStream, CancellationToken cancellationToken = default); bool Cancelled { get; } void Cancel(); @@ -28,7 +30,7 @@ namespace SharpCompress.Readers /// Moves to the next entry by reading more data from the underlying stream. This skips if data has not been read. /// /// - bool MoveToNextEntry(); + ValueTask MoveToNextEntry(); /// /// Opens the current entry as a stream that will decompress as it is read. diff --git a/src/SharpCompress/Readers/IReaderExtensions.cs b/src/SharpCompress/Readers/IReaderExtensions.cs index 502fc8ad..60d32798 100644 --- a/src/SharpCompress/Readers/IReaderExtensions.cs +++ b/src/SharpCompress/Readers/IReaderExtensions.cs @@ -1,63 +1,73 @@ -using System.IO; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; namespace SharpCompress.Readers { public static class IReaderExtensions { - public static void WriteEntryTo(this IReader reader, string filePath) + public static async ValueTask WriteEntryToAsync(this IReader reader, string filePath) { - using (Stream stream = File.Open(filePath, FileMode.Create, FileAccess.Write)) - { - reader.WriteEntryTo(stream); - } + await using Stream stream = File.Open(filePath, FileMode.Create, FileAccess.Write); + await reader.WriteEntryToAsync(stream); } - public static void WriteEntryTo(this IReader reader, FileInfo filePath) + public static async ValueTask WriteEntryTo(this IReader reader, FileInfo filePath) { - using (Stream stream = filePath.Open(FileMode.Create)) - { - reader.WriteEntryTo(stream); - } + await using Stream stream = filePath.Open(FileMode.Create); + await reader.WriteEntryToAsync(stream); } /// /// Extract all remaining unread entries to specific directory, retaining filename /// - public static void WriteAllToDirectory(this IReader reader, string destinationDirectory, + public static async ValueTask WriteAllToDirectoryAsync(this IReader reader, string destinationDirectory, ExtractionOptions? options = null) { - while (reader.MoveToNextEntry()) + while (await reader.MoveToNextEntry()) { - reader.WriteEntryToDirectory(destinationDirectory, options); + await reader.WriteEntryToDirectoryAsync(destinationDirectory, options); } } /// /// Extract to specific directory, retaining filename /// - public static void WriteEntryToDirectory(this IReader reader, string destinationDirectory, - ExtractionOptions? options = null) + public static ValueTask WriteEntryToDirectoryAsync(this IReader reader, string destinationDirectory, + ExtractionOptions? options = null, + CancellationToken cancellationToken = default) { - ExtractionMethods.WriteEntryToDirectory(reader.Entry, destinationDirectory, options, - reader.WriteEntryToFile); + if (reader.Entry is null) + { + throw new ArgumentException("Entry is null"); + } + return ExtractionMethods.WriteEntryToDirectoryAsync(reader.Entry, destinationDirectory, options, + async (x, o, ct) => + { + await reader.WriteEntryToFileAsync(x, o, ct); + }, cancellationToken); } /// /// Extract to specific file /// - public static void WriteEntryToFile(this IReader reader, + public static async ValueTask WriteEntryToFileAsync(this IReader reader, string destinationFileName, - ExtractionOptions? options = null) + ExtractionOptions? options = null, + CancellationToken cancellationToken = default) { - ExtractionMethods.WriteEntryToFile(reader.Entry, destinationFileName, options, - (x, fm) => + if (reader.Entry is null) + { + throw new ArgumentException("Entry is null"); + } + await ExtractionMethods.WriteEntryToFileAsync(reader.Entry, destinationFileName, options, + async (x, fm, ct) => { - using (FileStream fs = File.Open(destinationFileName, fm)) - { - reader.WriteEntryTo(fs); - } - }); + await using FileStream fs = File.Open(x, fm); + await reader.WriteEntryToAsync(fs, ct); + }, cancellationToken); } } } \ No newline at end of file diff --git a/src/SharpCompress/Readers/Zip/ZipReader.cs b/src/SharpCompress/Readers/Zip/ZipReader.cs index 54a759b2..5ee7f4fe 100644 --- a/src/SharpCompress/Readers/Zip/ZipReader.cs +++ b/src/SharpCompress/Readers/Zip/ZipReader.cs @@ -35,9 +35,9 @@ namespace SharpCompress.Readers.Zip #endregion Open - protected override IEnumerable GetEntries(Stream stream) + protected override async IAsyncEnumerable GetEntries(Stream stream) { - foreach (ZipHeader h in _headerFactory.ReadStreamHeader(stream)) + await foreach (ZipHeader h in _headerFactory.ReadStreamHeader(stream)) { if (h != null) { diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 372a488b..22766b5e 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -32,6 +32,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 79bb54ee..95aca23e 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -2,6 +2,7 @@ using System; using System.Buffers; using System.Collections.Generic; using System.IO; +using System.Threading; using System.Threading.Tasks; using SharpCompress.Readers; @@ -231,20 +232,20 @@ namespace SharpCompress return sTime.AddSeconds(unixtime); } - public static async Task TransferToAsync(this Stream source, Stream destination) + public static async Task TransferToAsync(this Stream source, Stream destination, CancellationToken cancellationToken) { using var buffer = MemoryPool.Shared.Rent(81920); long total = 0; while (true) { - int bytesRead = await source.ReadAsync(buffer.Memory); + int bytesRead = await source.ReadAsync(buffer.Memory, cancellationToken); if (bytesRead == 0) { break; } total += bytesRead; - await destination.WriteAsync(buffer.Memory); + await destination.WriteAsync(buffer.Memory, cancellationToken); } return total; } @@ -255,7 +256,8 @@ namespace SharpCompress try { long total = 0; - while (ReadTransferBlock(source, array, out int count)) + var count = 0; + while ((count = source.Read(array, 0, array.Length)) != 0) { total += count; destination.Write(array, 0, count); @@ -268,17 +270,18 @@ namespace SharpCompress } } - public static long TransferTo(this Stream source, Stream destination, Common.Entry entry, IReaderExtractionListener readerExtractionListener) + public static async ValueTask TransferToAsync(this Stream source, Stream destination, Common.Entry entry, IReaderExtractionListener readerExtractionListener, CancellationToken cancellationToken) { byte[] array = GetTransferByteArray(); try { var iterations = 0; long total = 0; - while (ReadTransferBlock(source, array, out int count)) + var count = 0; + while ((count = await source.ReadAsync(array, 0, array.Length, cancellationToken)) != 0) { total += count; - destination.Write(array, 0, count); + await destination.WriteAsync(array, 0, count, cancellationToken); iterations++; readerExtractionListener.FireEntryExtractionProgress(entry, total, iterations); } @@ -290,11 +293,6 @@ namespace SharpCompress } } - private static bool ReadTransferBlock(Stream source, byte[] array, out int count) - { - return (count = source.Read(array, 0, array.Length)) != 0; - } - private static byte[] GetTransferByteArray() { return ArrayPool.Shared.Rent(81920); diff --git a/src/SharpCompress/Writers/GZip/GZipWriter.cs b/src/SharpCompress/Writers/GZip/GZipWriter.cs index 1147b686..bdfc54ce 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriter.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriter.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Compressors; using SharpCompress.Compressors.Deflate; @@ -18,22 +20,18 @@ namespace SharpCompress.Writers.GZip { destination = new NonDisposingStream(destination); } - InitalizeStream(new GZipStream(destination, CompressionMode.Compress, + InitializeStream(new GZipStream(destination, CompressionMode.Compress, options?.CompressionLevel ?? CompressionLevel.Default, WriterOptions.ArchiveEncoding.GetEncoding())); } - protected override void Dispose(bool isDisposing) + protected override ValueTask DisposeAsyncCore() { - if (isDisposing) - { - //dispose here to finish the GZip, GZip won't close the underlying stream - OutputStream.Dispose(); - } - base.Dispose(isDisposing); + //dispose here to finish the GZip, GZip won't close the underlying stream + return OutputStream.DisposeAsync(); } - public override void Write(string filename, Stream source, DateTime? modificationTime) + public override async Task WriteAsync(string filename, Stream source, DateTime? modificationTime, CancellationToken cancellationToken) { if (_wroteToStream) { @@ -42,7 +40,7 @@ namespace SharpCompress.Writers.GZip GZipStream stream = (GZipStream)OutputStream; stream.FileName = filename; stream.LastModified = modificationTime; - source.TransferTo(stream); + await source.TransferToAsync(stream); _wroteToStream = true; } } diff --git a/src/SharpCompress/Writers/IWriterExtensions.cs b/src/SharpCompress/Writers/IWriterExtensions.cs index 376728af..b2022df9 100644 --- a/src/SharpCompress/Writers/IWriterExtensions.cs +++ b/src/SharpCompress/Writers/IWriterExtensions.cs @@ -2,17 +2,19 @@ using System.IO; using System.Linq; using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; namespace SharpCompress.Writers { public static class IWriterExtensions { - public static void Write(this IWriter writer, string entryPath, Stream source) + public static Task WriteAsync(this IWriter writer, string entryPath, Stream source, CancellationToken cancellationToken = default) { - writer.Write(entryPath, source, null); + return writer.WriteAsync(entryPath, source, null, cancellationToken); } - public static void Write(this IWriter writer, string entryPath, FileInfo source) + public static async Task WriteAsync(this IWriter writer, string entryPath, FileInfo source, CancellationToken cancellationToken = default) { if (!source.Exists) { @@ -20,25 +22,25 @@ namespace SharpCompress.Writers } using (var stream = source.OpenRead()) { - writer.Write(entryPath, stream, source.LastWriteTime); + await writer.WriteAsync(entryPath, stream, source.LastWriteTime, cancellationToken); } } - public static void Write(this IWriter writer, string entryPath, string source) + public static Task WriteAsync(this IWriter writer, string entryPath, string source, CancellationToken cancellationToken = default) { - writer.Write(entryPath, new FileInfo(source)); + return writer.WriteAsync(entryPath, new FileInfo(source), cancellationToken); } - public static void WriteAll(this IWriter writer, string directory, string searchPattern = "*", SearchOption option = SearchOption.TopDirectoryOnly) + public static Task WriteAllAsync(this IWriter writer, string directory, string searchPattern = "*", SearchOption option = SearchOption.TopDirectoryOnly, CancellationToken cancellationToken = default) { - writer.WriteAll(directory, searchPattern, null, option); + return writer.WriteAllAsync(directory, searchPattern, null, option, cancellationToken); } - public static void WriteAll(this IWriter writer, - string directory, - string searchPattern = "*", - Expression>? fileSearchFunc = null, - SearchOption option = SearchOption.TopDirectoryOnly) + public static async Task WriteAllAsync(this IWriter writer, + string directory, + string searchPattern = "*", + Expression>? fileSearchFunc = null, + SearchOption option = SearchOption.TopDirectoryOnly, CancellationToken cancellationToken = default) { if (!Directory.Exists(directory)) { @@ -51,7 +53,7 @@ namespace SharpCompress.Writers } foreach (var file in Directory.EnumerateFiles(directory, searchPattern, option).Where(fileSearchFunc.Compile())) { - writer.Write(file.Substring(directory.Length), file); + await writer.WriteAsync(file.Substring(directory.Length), file, cancellationToken); } } } diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index 74ae7145..8fde488f 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -1,8 +1,11 @@ using System; +using System.Buffers; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Text; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.Zip; using SharpCompress.Common.Zip.Headers; @@ -42,7 +45,7 @@ namespace SharpCompress.Writers.Zip { destination = new NonDisposingStream(destination); } - InitalizeStream(destination); + InitializeStream(destination); } private PpmdProperties PpmdProperties @@ -53,18 +56,15 @@ namespace SharpCompress.Writers.Zip } } - protected override void Dispose(bool isDisposing) + protected override async ValueTask DisposeAsyncCore() { - if (isDisposing) + ulong size = 0; + foreach (ZipCentralDirectoryEntry entry in entries) { - ulong size = 0; - foreach (ZipCentralDirectoryEntry entry in entries) - { - size += entry.Write(OutputStream); - } - WriteEndRecord(size); + size += entry.Write(OutputStream); } - base.Dispose(isDisposing); + await WriteEndRecordAsync(size); + await base.DisposeAsyncCore(); } private static ZipCompressionMethod ToZipCompressionMethod(CompressionType compressionType) @@ -96,23 +96,21 @@ namespace SharpCompress.Writers.Zip } } - public override void Write(string entryPath, Stream source, DateTime? modificationTime) + public override Task WriteAsync(string filename, Stream source, DateTime? modificationTime, CancellationToken cancellationToken) { - Write(entryPath, source, new ZipWriterEntryOptions() - { - ModificationDateTime = modificationTime - }); + return WriteAsync(filename, source, new ZipWriterEntryOptions() + { + ModificationDateTime = modificationTime + }); } - public void Write(string entryPath, Stream source, ZipWriterEntryOptions zipWriterEntryOptions) + public async Task WriteAsync(string entryPath, Stream source, ZipWriterEntryOptions zipWriterEntryOptions) { - using (Stream output = WriteToStream(entryPath, zipWriterEntryOptions)) - { - source.TransferTo(output); - } + await using Stream output = await WriteToStreamAsync(entryPath, zipWriterEntryOptions); + await source.TransferToAsync(output); } - public Stream WriteToStream(string entryPath, ZipWriterEntryOptions options) + public async Task WriteToStreamAsync(string entryPath, ZipWriterEntryOptions options) { var compression = ToZipCompressionMethod(options.CompressionType ?? compressionType); @@ -132,7 +130,7 @@ namespace SharpCompress.Writers.Zip useZip64 = options.EnableZip64.Value; } - var headersize = (uint)WriteHeader(entryPath, options, entry, useZip64); + var headersize = (uint)(await WriteHeaderAsync(entryPath, options, entry, useZip64)); streamPosition += headersize; return new ZipWritingStream(this, OutputStream, entry, compression, options.DeflateCompressionLevel ?? compressionLevel); @@ -151,7 +149,7 @@ namespace SharpCompress.Writers.Zip return filename.Trim('/'); } - private int WriteHeader(string filename, ZipWriterEntryOptions zipWriterEntryOptions, ZipCentralDirectoryEntry entry, bool useZip64) + private async Task WriteHeaderAsync(string filename, ZipWriterEntryOptions zipWriterEntryOptions, ZipCentralDirectoryEntry entry, bool useZip64) { // We err on the side of caution until the zip specification clarifies how to support this if (!OutputStream.CanSeek && useZip64) @@ -163,23 +161,32 @@ namespace SharpCompress.Writers.Zip byte[] encodedFilename = WriterOptions.ArchiveEncoding.Encode(filename); // TODO: Use stackalloc when we exclusively support netstandard2.1 or higher - byte[] intBuf = new byte[4]; - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, ZipHeaderFactory.ENTRY_HEADER_BYTES); - OutputStream.Write(intBuf, 0, 4); + using var intBuf = MemoryPool.Shared.Rent(4); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, ZipHeaderFactory.ENTRY_HEADER_BYTES); + await OutputStream.WriteAsync(intBuf.Memory); if (explicitZipCompressionInfo == ZipCompressionMethod.Deflate) { if (OutputStream.CanSeek && useZip64) { - OutputStream.Write(stackalloc byte[] { 45, 0 }); //smallest allowed version for zip64 + using var buf1 = MemoryPool.Shared.Rent(2); + buf1.Memory.Span[0] = 45; + buf1.Memory.Span[1] = 0; + await OutputStream.WriteAsync(buf1.Memory); //smallest allowed version for zip64 } else { - OutputStream.Write(stackalloc byte[] { 20, 0 }); //older version which is more compatible + using var buf1 = MemoryPool.Shared.Rent(2); + buf1.Memory.Span[0] = 20; + buf1.Memory.Span[1] = 0; + await OutputStream.WriteAsync(buf1.Memory); //older version which is more compatible } } else { - OutputStream.Write(stackalloc byte[] { 63, 0 }); //version says we used PPMd or LZMA + using var buf1 = MemoryPool.Shared.Rent(2); + buf1.Memory.Span[0] = 63; + buf1.Memory.Span[1] = 0; + await OutputStream.WriteAsync(buf1.Memory); //version says we used PPMd or LZMA } HeaderFlags flags = Equals(WriterOptions.ArchiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.Efs : 0; if (!OutputStream.CanSeek) @@ -192,19 +199,21 @@ namespace SharpCompress.Writers.Zip } } - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)flags); - OutputStream.Write(intBuf, 0, 2); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)explicitZipCompressionInfo); - OutputStream.Write(intBuf, 0, 2); // zipping method - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, zipWriterEntryOptions.ModificationDateTime.DateTimeToDosTime()); - OutputStream.Write(intBuf, 0, 4); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)flags); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0,2)); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)explicitZipCompressionInfo); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0,2)); // zipping method + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, zipWriterEntryOptions.ModificationDateTime.DateTimeToDosTime()); + await OutputStream.WriteAsync(intBuf.Memory); // zipping date and time - OutputStream.Write(stackalloc byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); + using var buf2 = MemoryPool.Shared.Rent(12); + buf2.Memory.Span.Clear(); + await OutputStream.WriteAsync(buf2.Memory); // unused CRC, un/compressed size, updated later - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedFilename.Length); - OutputStream.Write(intBuf, 0, 2); // filename length + BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)encodedFilename.Length); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0,2));// filename length var extralength = 0; if (OutputStream.CanSeek && useZip64) @@ -212,31 +221,33 @@ namespace SharpCompress.Writers.Zip extralength = 2 + 2 + 8 + 8; } - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)extralength); - OutputStream.Write(intBuf, 0, 2); // extra length - OutputStream.Write(encodedFilename, 0, encodedFilename.Length); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)extralength); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0,2));// extra length + await OutputStream.WriteAsync(encodedFilename, 0, encodedFilename.Length); if (extralength != 0) { - OutputStream.Write(new byte[extralength], 0, extralength); // reserve space for zip64 data + using var buf3 = MemoryPool.Shared.Rent(extralength); + buf2.Memory.Span.Clear(); + await OutputStream.WriteAsync(buf3.Memory); // reserve space for zip64 data entry.Zip64HeaderOffset = (ushort)(6 + 2 + 2 + 4 + 12 + 2 + 2 + encodedFilename.Length); } return 6 + 2 + 2 + 4 + 12 + 2 + 2 + encodedFilename.Length + extralength; } - private void WriteFooter(uint crc, uint compressed, uint uncompressed) + private async Task WriteFooterAsync(uint crc, uint compressed, uint uncompressed) { - byte[] intBuf = new byte[4]; - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, crc); - OutputStream.Write(intBuf, 0, 4); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, compressed); - OutputStream.Write(intBuf, 0, 4); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, uncompressed); - OutputStream.Write(intBuf, 0, 4); + using var intBuf = MemoryPool.Shared.Rent(4); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, crc); + await OutputStream.WriteAsync(intBuf.Memory); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, compressed); + await OutputStream.WriteAsync(intBuf.Memory); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, uncompressed); + await OutputStream.WriteAsync(intBuf.Memory); } - private void WriteEndRecord(ulong size) + private async Task WriteEndRecordAsync(ulong size) { var zip64 = isZip64 || entries.Count > ushort.MaxValue || streamPosition >= uint.MaxValue || size >= uint.MaxValue; @@ -244,66 +255,82 @@ namespace SharpCompress.Writers.Zip var sizevalue = size >= uint.MaxValue ? uint.MaxValue : (uint)size; var streampositionvalue = streamPosition >= uint.MaxValue ? uint.MaxValue : (uint)streamPosition; - byte[] intBuf = new byte[8]; + using var intBuf = MemoryPool.Shared.Rent(8); if (zip64) { var recordlen = 2 + 2 + 4 + 4 + 8 + 8 + 8 + 8; // Write zip64 end of central directory record - OutputStream.Write(stackalloc byte[] { 80, 75, 6, 6 }); + var s = intBuf.Memory.Slice(0, 4); + s.Span[0] = 80; + s.Span[1] = 75; + s.Span[2] = 6; + s.Span[3] = 6; + await OutputStream.WriteAsync(s); - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)recordlen); - OutputStream.Write(intBuf, 0, 8); // Size of zip64 end of central directory record - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0); - OutputStream.Write(intBuf, 0, 2); // Made by - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 45); - OutputStream.Write(intBuf, 0, 2); // Version needed + BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, (ulong)recordlen); + await OutputStream.WriteAsync(intBuf.Memory);// Size of zip64 end of central directory record + BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, 0); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2)); // Made by + BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, 45); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2)); // Version needed - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 0); - OutputStream.Write(intBuf, 0, 4); // Disk number - OutputStream.Write(intBuf, 0, 4); // Central dir disk + intBuf.Memory.Span.Clear(); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); // Disk number + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); // Central dir disk // TODO: entries.Count is int, so max 2^31 files - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)entries.Count); - OutputStream.Write(intBuf, 0, 8); // Entries in this disk - OutputStream.Write(intBuf, 0, 8); // Total entries - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, size); - OutputStream.Write(intBuf, 0, 8); // Central Directory size - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)streamPosition); - OutputStream.Write(intBuf, 0, 8); // Disk offset + BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, (ulong)entries.Count); + await OutputStream.WriteAsync(intBuf.Memory); // Entries in this disk + await OutputStream.WriteAsync(intBuf.Memory); // Total entries + BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, size); + await OutputStream.WriteAsync(intBuf.Memory); // Central Directory size + BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, (ulong)streamPosition); + await OutputStream.WriteAsync(intBuf.Memory); // Disk offset // Write zip64 end of central directory locator - OutputStream.Write(stackalloc byte[] { 80, 75, 6, 7 }); + s = intBuf.Memory.Slice(0, 4); + s.Span[0] = 80; + s.Span[1] = 75; + s.Span[2] = 6; + s.Span[3] = 7; + await OutputStream.WriteAsync(s); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 0); - OutputStream.Write(intBuf, 0, 4); // Entry disk - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)streamPosition + size); - OutputStream.Write(intBuf, 0, 8); // Offset to the zip64 central directory - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 0); - OutputStream.Write(intBuf, 0, 4); // Number of disks + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, 0); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); // Entry disk + BinaryPrimitives.WriteUInt64LittleEndian(intBuf.Memory.Span, (ulong)streamPosition + size); + await OutputStream.WriteAsync(intBuf.Memory); // Offset to the zip64 central directory + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, 0); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); // Number of disks streamPosition += recordlen + (4 + 4 + 8 + 4); streampositionvalue = streamPosition >= uint.MaxValue ? uint.MaxValue : (uint)streampositionvalue; } // Write normal end of central directory record - OutputStream.Write(stackalloc byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)entries.Count); - OutputStream.Write(intBuf, 0, 2); - OutputStream.Write(intBuf, 0, 2); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, sizevalue); - OutputStream.Write(intBuf, 0, 4); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, streampositionvalue); - OutputStream.Write(intBuf, 0, 4); + intBuf.Memory.Span.Clear(); + var x = intBuf.Memory.Slice(0, 4); + x.Span[0] = 80; + x.Span[1] = 75; + x.Span[2] = 5; + x.Span[3] = 6; + await OutputStream.WriteAsync(intBuf.Memory); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)entries.Count); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2)); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2));//TODO: this is twice? + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, sizevalue); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, streampositionvalue); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 4)); byte[] encodedComment = WriterOptions.ArchiveEncoding.Encode(zipComment); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedComment.Length); - OutputStream.Write(intBuf, 0, 2); - OutputStream.Write(encodedComment, 0, encodedComment.Length); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf.Memory.Span, (ushort)encodedComment.Length); + await OutputStream.WriteAsync(intBuf.Memory.Slice(0, 2)); + await OutputStream.WriteAsync(encodedComment.AsMemory()); } #region Nested type: ZipWritingStream - internal class ZipWritingStream : Stream + private class ZipWritingStream : Stream { private readonly CRC32 crc = new CRC32(); private readonly ZipCentralDirectoryEntry entry; @@ -383,7 +410,7 @@ namespace SharpCompress.Writers.Zip } } - protected override void Dispose(bool disposing) + public override async ValueTask DisposeAsync() { if (isDisposed) { @@ -392,95 +419,92 @@ namespace SharpCompress.Writers.Zip isDisposed = true; - base.Dispose(disposing); - if (disposing) + await base.DisposeAsync(); + await writeStream.DisposeAsync(); + + if (limitsExceeded) { - writeStream.Dispose(); - - if (limitsExceeded) - { - // We have written invalid data into the archive, - // so we destroy it now, instead of allowing the user to continue - // with a defunct archive - originalStream.Dispose(); - return; - } - - entry.Crc = (uint)crc.Crc32Result; - entry.Compressed = counting!.Count; - entry.Decompressed = decompressed; - - var zip64 = entry.Compressed >= uint.MaxValue || entry.Decompressed >= uint.MaxValue; - var compressedvalue = zip64 ? uint.MaxValue : (uint)counting.Count; - var decompressedvalue = zip64 ? uint.MaxValue : (uint)entry.Decompressed; - - if (originalStream.CanSeek) - { - originalStream.Position = (long)(entry.HeaderOffset + 6); - originalStream.WriteByte(0); - - if (counting.Count == 0 && entry.Decompressed == 0) - { - // set compression to STORED for zero byte files (no compression data) - originalStream.Position = (long)(entry.HeaderOffset + 8); - originalStream.WriteByte(0); - originalStream.WriteByte(0); - } - - originalStream.Position = (long)(entry.HeaderOffset + 14); - - writer.WriteFooter(entry.Crc, compressedvalue, decompressedvalue); - - // Ideally, we should not throw from Dispose() - // We should not get here as the Write call checks the limits - if (zip64 && entry.Zip64HeaderOffset == 0) - { - throw new NotSupportedException("Attempted to write a stream that is larger than 4GiB without setting the zip64 option"); - } - - // If we have pre-allocated space for zip64 data, - // fill it out, even if it is not required - if (entry.Zip64HeaderOffset != 0) - { - originalStream.Position = (long)(entry.HeaderOffset + entry.Zip64HeaderOffset); - byte[] intBuf = new byte[8]; - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0x0001); - originalStream.Write(intBuf, 0, 2); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 8 + 8); - originalStream.Write(intBuf, 0, 2); - - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, entry.Decompressed); - originalStream.Write(intBuf, 0, 8); - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, entry.Compressed); - originalStream.Write(intBuf, 0, 8); - } - - originalStream.Position = writer.streamPosition + (long)entry.Compressed; - writer.streamPosition += (long)entry.Compressed; - } - else - { - // We have a streaming archive, so we should add a post-data-descriptor, - // but we cannot as it does not hold the zip64 values - // Throwing an exception until the zip specification is clarified - - // Ideally, we should not throw from Dispose() - // We should not get here as the Write call checks the limits - if (zip64) - { - throw new NotSupportedException("Streams larger than 4GiB are not supported for non-seekable streams"); - } - - byte[] intBuf = new byte[4]; - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, ZipHeaderFactory.POST_DATA_DESCRIPTOR); - originalStream.Write(intBuf, 0, 4); - writer.WriteFooter(entry.Crc, - compressedvalue, - decompressedvalue); - writer.streamPosition += (long)entry.Compressed + 16; - } - writer.entries.Add(entry); + // We have written invalid data into the archive, + // so we destroy it now, instead of allowing the user to continue + // with a defunct archive + await originalStream.DisposeAsync(); + return; } + + entry.Crc = (uint)crc.Crc32Result; + entry.Compressed = counting!.Count; + entry.Decompressed = decompressed; + + var zip64 = entry.Compressed >= uint.MaxValue || entry.Decompressed >= uint.MaxValue; + var compressedvalue = zip64 ? uint.MaxValue : (uint)counting.Count; + var decompressedvalue = zip64 ? uint.MaxValue : (uint)entry.Decompressed; + + if (originalStream.CanSeek) + { + originalStream.Position = (long)(entry.HeaderOffset + 6); + originalStream.WriteByte(0); + + if (counting.Count == 0 && entry.Decompressed == 0) + { + // set compression to STORED for zero byte files (no compression data) + originalStream.Position = (long)(entry.HeaderOffset + 8); + originalStream.WriteByte(0); + originalStream.WriteByte(0); + } + + originalStream.Position = (long)(entry.HeaderOffset + 14); + + await writer.WriteFooterAsync(entry.Crc, compressedvalue, decompressedvalue); + + // Ideally, we should not throw from Dispose() + // We should not get here as the Write call checks the limits + if (zip64 && entry.Zip64HeaderOffset == 0) + { + throw new NotSupportedException("Attempted to write a stream that is larger than 4GiB without setting the zip64 option"); + } + + // If we have pre-allocated space for zip64 data, + // fill it out, even if it is not required + if (entry.Zip64HeaderOffset != 0) + { + originalStream.Position = (long)(entry.HeaderOffset + entry.Zip64HeaderOffset); + byte[] intBuf = new byte[8]; + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0x0001); + await originalStream.WriteAsync(intBuf, 0, 2); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 8 + 8); + await originalStream.WriteAsync(intBuf, 0, 2); + + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, entry.Decompressed); + await originalStream.WriteAsync(intBuf, 0, 8); + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, entry.Compressed); + await originalStream.WriteAsync(intBuf, 0, 8); + } + + originalStream.Position = writer.streamPosition + (long)entry.Compressed; + writer.streamPosition += (long)entry.Compressed; + } + else + { + // We have a streaming archive, so we should add a post-data-descriptor, + // but we cannot as it does not hold the zip64 values + // Throwing an exception until the zip specification is clarified + + // Ideally, we should not throw from Dispose() + // We should not get here as the Write call checks the limits + if (zip64) + { + throw new NotSupportedException("Streams larger than 4GiB are not supported for non-seekable streams"); + } + + using var intBuf = MemoryPool.Shared.Rent(4); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf.Memory.Span, ZipHeaderFactory.POST_DATA_DESCRIPTOR); + await originalStream.WriteAsync(intBuf.Memory); + await writer.WriteFooterAsync(entry.Crc, + compressedvalue, + decompressedvalue); + writer.streamPosition += (long)entry.Compressed + 16; + } + writer.entries.Add(entry); } public override void Flush() @@ -488,6 +512,11 @@ namespace SharpCompress.Writers.Zip writeStream.Flush(); } + public override Task FlushAsync(CancellationToken cancellationToken) + { + return writeStream.FlushAsync(cancellationToken); + } + public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); @@ -504,6 +533,11 @@ namespace SharpCompress.Writers.Zip } public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { // We check the limits first, because we can keep the archive consistent // if we can prevent the writes from happening @@ -518,7 +552,7 @@ namespace SharpCompress.Writers.Zip decompressed += (uint)count; crc.SlurpBlock(buffer, offset, count); - writeStream.Write(buffer, offset, count); + await writeStream.WriteAsync(buffer, offset, count, cancellationToken); if (entry.Zip64HeaderOffset == 0) { diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs index 5f4a7c22..b83ded23 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Threading.Tasks; using SharpCompress.Archives; using SharpCompress.Archives.GZip; using Xunit; @@ -56,14 +57,14 @@ namespace SharpCompress.Test.GZip [Fact] - public void GZip_Archive_NoAdd() + public async Task GZip_Archive_NoAdd() { string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) + await using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) using (var archive = GZipArchive.Open(stream)) { Assert.Throws(() => archive.AddEntry("jpg\\test.jpg", jpg)); - archive.SaveTo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz")); + await archive.SaveToAsync(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz")); } } diff --git a/tests/SharpCompress.Test/GZip/GZipWriterTests.cs b/tests/SharpCompress.Test/GZip/GZipWriterTests.cs index ec64dcc0..cf1f1f2d 100644 --- a/tests/SharpCompress.Test/GZip/GZipWriterTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipWriterTests.cs @@ -21,7 +21,7 @@ namespace SharpCompress.Test.GZip using (Stream stream = File.Open(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), FileMode.OpenOrCreate, FileAccess.Write)) await using (var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.GZip)) { - writer.Write("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); + await writer.WriteAsync("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); } CompareArchivesByPath(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); @@ -33,7 +33,7 @@ namespace SharpCompress.Test.GZip using (Stream stream = File.Open(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), FileMode.OpenOrCreate, FileAccess.Write)) await using (var writer = new GZipWriter(stream)) { - writer.Write("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); + await writer.WriteAsync("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); } CompareArchivesByPath(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); @@ -58,7 +58,7 @@ namespace SharpCompress.Test.GZip await using (var writer = new GZipWriter(stream)) { var path = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"); - writer.Write(path, path); //covers issue #532 + await writer.WriteAsync(path, path); //covers issue #532 } CompareArchivesByPath(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index fbb420d6..6c2a8f92 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -21,4 +21,19 @@ + + + + + + + + + + + + + + + diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index ae1a94f7..6a098b82 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -125,7 +125,7 @@ namespace SharpCompress.Test.Tar } [Fact] - public async Task Tar_UstarArchivePathReadLongName() + public void Tar_UstarArchivePathReadLongName() { string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "ustar with long names.tar"); using var archive = TarArchive.Open(unmodified); @@ -154,7 +154,7 @@ namespace SharpCompress.Test.Tar { Default = Encoding.GetEncoding(866) }; - archive.SaveTo(scratchPath, twopt); + await archive.SaveToAsync(scratchPath, twopt); } CompareArchivesByPath(unmodified, scratchPath); } @@ -169,7 +169,7 @@ namespace SharpCompress.Test.Tar using (var archive = TarArchive.Open(unmodified)) { archive.AddEntry("jpg\\test.jpg", jpg); - archive.SaveTo(scratchPath, CompressionType.None); + await archive.SaveToAsync(scratchPath, CompressionType.None); } CompareArchivesByPath(modified, scratchPath); } @@ -185,13 +185,13 @@ namespace SharpCompress.Test.Tar { var entry = archive.Entries.Single(x => x.Key.EndsWith("jpg")); archive.RemoveEntry(entry); - archive.SaveTo(scratchPath, CompressionType.None); + await archive.SaveToAsync(scratchPath, CompressionType.None); } CompareArchivesByPath(modified, scratchPath); } [Fact] - public async Task Tar_Containing_Rar_Archive() + public void Tar_Containing_Rar_Archive() { string archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.ContainsRar.tar"); using Stream stream = File.OpenRead(archiveFullPath); @@ -200,7 +200,7 @@ namespace SharpCompress.Test.Tar } [Fact] - public async Task Tar_Empty_Archive() + public void Tar_Empty_Archive() { string archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.Empty.tar"); using Stream stream = File.OpenRead(archiveFullPath); @@ -252,9 +252,9 @@ namespace SharpCompress.Test.Tar await using (var tarWriter = new TarWriter(memoryStream, tarWriterOptions)) using (var testFileStream = new MemoryStream(testBytes)) { - tarWriter.Write("test1.txt", testFileStream); + await tarWriter.WriteAsync("test1.txt", testFileStream); testFileStream.Position = 0; - tarWriter.Write("test2.txt", testFileStream); + await tarWriter.WriteAsync("test2.txt", testFileStream); } memoryStream.Position = 0; diff --git a/tests/SharpCompress.Test/WriterTests.cs b/tests/SharpCompress.Test/WriterTests.cs index d9ef88f0..cda40f83 100644 --- a/tests/SharpCompress.Test/WriterTests.cs +++ b/tests/SharpCompress.Test/WriterTests.cs @@ -30,7 +30,7 @@ namespace SharpCompress.Test await using (var writer = WriterFactory.Open(stream, type, writerOptions)) { - writer.WriteAll(ORIGINAL_FILES_PATH, "*", SearchOption.AllDirectories); + await writer.WriteAllAsync(ORIGINAL_FILES_PATH, "*", SearchOption.AllDirectories); } } CompareArchivesByPath(Path.Combine(SCRATCH2_FILES_PATH, archive), diff --git a/tests/SharpCompress.Test/Zip/Zip64Tests.cs b/tests/SharpCompress.Test/Zip/Zip64Tests.cs index 48630e5e..0bc43fe7 100644 --- a/tests/SharpCompress.Test/Zip/Zip64Tests.cs +++ b/tests/SharpCompress.Test/Zip/Zip64Tests.cs @@ -24,47 +24,47 @@ namespace SharpCompress.Test.Zip private const long FOUR_GB_LIMIT = ((long)uint.MaxValue) + 1; [Trait("format", "zip64")] - public void Zip64_Single_Large_File() + public async Task Zip64_Single_Large_File() { // One single file, requires zip64 - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); + await RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); } [Trait("format", "zip64")] - public void Zip64_Two_Large_Files() + public async Task Zip64_Two_Large_Files() { // One single file, requires zip64 - RunSingleTest(2, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); + await RunSingleTest(2, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); } [Trait("format", "zip64")] - public void Zip64_Two_Small_files() + public async Task Zip64_Two_Small_files() { // Multiple files, does not require zip64 - RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: false); + await RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: false); } [Trait("format", "zip64")] - public void Zip64_Two_Small_files_stream() + public async Task Zip64_Two_Small_files_stream() { // Multiple files, does not require zip64, and works with streams - RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: true); + await RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: true); } [Trait("format", "zip64")] - public void Zip64_Two_Small_Files_Zip64() + public async Task Zip64_Two_Small_Files_Zip64() { // Multiple files, use zip64 even though it is not required - RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: true, forward_only: false); + await RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: true, forward_only: false); } [Trait("format", "zip64")] - public void Zip64_Single_Large_File_Fail() + public async Task Zip64_Single_Large_File_Fail() { try { // One single file, should fail - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: false); + await RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: false); throw new Exception("Test did not fail?"); } catch (NotSupportedException) @@ -73,12 +73,12 @@ namespace SharpCompress.Test.Zip } [Trait("zip64", "true")] - public void Zip64_Single_Large_File_Zip64_Streaming_Fail() + public async Task Zip64_Single_Large_File_Zip64_Streaming_Fail() { try { // One single file, should fail (fast) with zip64 - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: true); + await RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: true); throw new Exception("Test did not fail?"); } catch (NotSupportedException) @@ -87,12 +87,12 @@ namespace SharpCompress.Test.Zip } [Trait("zip64", "true")] - public void Zip64_Single_Large_File_Streaming_Fail() + public async Task Zip64_Single_Large_File_Streaming_Fail() { try { // One single file, should fail once the write discovers the problem - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: true); + await RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: true); throw new Exception("Test did not fail?"); } catch (NotSupportedException) @@ -100,7 +100,7 @@ namespace SharpCompress.Test.Zip } } - public void RunSingleTest(long files, long filesize, bool set_zip64, bool forward_only, long write_chunk_size = 1024 * 1024, string filename = "zip64-test.zip") + public async Task RunSingleTest(long files, long filesize, bool set_zip64, bool forward_only, long write_chunk_size = 1024 * 1024, string filename = "zip64-test.zip") { filename = Path.Combine(SCRATCH2_FILES_PATH, filename); @@ -111,7 +111,7 @@ namespace SharpCompress.Test.Zip if (!File.Exists(filename)) { - CreateZipArchive(filename, files, filesize, write_chunk_size, set_zip64, forward_only); + await CreateZipArchive(filename, files, filesize, write_chunk_size, set_zip64, forward_only); } var resForward = ReadForwardOnly(filename); @@ -154,13 +154,13 @@ namespace SharpCompress.Test.Zip for (var i = 0; i < files; i++) { - using (var str = zipWriter.WriteToStream(i.ToString(), eo)) + await using (var str = await zipWriter.WriteToStreamAsync(i.ToString(), eo)) { var left = filesize; while (left > 0) { var b = (int)Math.Min(left, data.Length); - str.Write(data, 0, b); + await str.WriteAsync(data, 0, b); left -= b; } } diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index 59581b7a..19ba8b98 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -153,7 +153,7 @@ namespace SharpCompress.Test.Zip } [Fact] - public void Zip_Random_Write_Remove() + public async Task Zip_Random_Write_Remove() { string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Zip.deflate.mod.zip"); string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); @@ -167,13 +167,13 @@ namespace SharpCompress.Test.Zip WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate); writerOptions.ArchiveEncoding.Default = Encoding.GetEncoding(866); - archive.SaveTo(scratchPath, writerOptions); + await archive.SaveToAsync(scratchPath, writerOptions); } CompareArchivesByPath(modified, scratchPath, Encoding.GetEncoding(866)); } [Fact] - public void Zip_Random_Write_Add() + public async Task Zip_Random_Write_Add() { string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Zip.deflate.mod.zip"); @@ -187,13 +187,13 @@ namespace SharpCompress.Test.Zip WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate); writerOptions.ArchiveEncoding.Default = Encoding.GetEncoding(866); - archive.SaveTo(scratchPath, writerOptions); + await archive.SaveToAsync(scratchPath, writerOptions); } CompareArchivesByPath(modified, scratchPath, Encoding.GetEncoding(866)); } [Fact] - public void Zip_Save_Twice() + public async Task Zip_Save_Twice() { string scratchPath1 = Path.Combine(SCRATCH_FILES_PATH, "a.zip"); string scratchPath2 = Path.Combine(SCRATCH_FILES_PATH, "b.zip"); @@ -203,8 +203,8 @@ namespace SharpCompress.Test.Zip string str = "test.txt"; var source = new MemoryStream(Encoding.UTF8.GetBytes(str)); arc.AddEntry("test.txt", source, true, source.Length); - arc.SaveTo(scratchPath1, CompressionType.Deflate); - arc.SaveTo(scratchPath2, CompressionType.Deflate); + await arc.SaveToAsync(scratchPath1, CompressionType.Deflate); + await arc.SaveToAsync(scratchPath2, CompressionType.Deflate); } Assert.Equal(new FileInfo(scratchPath1).Length, new FileInfo(scratchPath2).Length); @@ -238,7 +238,7 @@ namespace SharpCompress.Test.Zip } [Fact] - public void Zip_Create_Same_Stream() + public async Task Zip_Create_Same_Stream() { string scratchPath1 = Path.Combine(SCRATCH_FILES_PATH, "a.zip"); string scratchPath2 = Path.Combine(SCRATCH_FILES_PATH, "b.zip"); @@ -249,8 +249,8 @@ namespace SharpCompress.Test.Zip { arc.AddEntry("1.txt", stream, false, stream.Length); arc.AddEntry("2.txt", stream, false, stream.Length); - arc.SaveTo(scratchPath1, CompressionType.Deflate); - arc.SaveTo(scratchPath2, CompressionType.Deflate); + await arc.SaveToAsync(scratchPath1, CompressionType.Deflate); + await arc.SaveToAsync(scratchPath2, CompressionType.Deflate); } } @@ -258,7 +258,7 @@ namespace SharpCompress.Test.Zip } [Fact] - public void Zip_Create_New() + public async Task Zip_Create_New() { foreach (var file in Directory.EnumerateFiles(ORIGINAL_FILES_PATH, "*.*", SearchOption.AllDirectories)) { @@ -285,7 +285,7 @@ namespace SharpCompress.Test.Zip WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate); writerOptions.ArchiveEncoding.Default = Encoding.GetEncoding(866); - archive.SaveTo(scratchPath, writerOptions); + await archive.SaveToAsync(scratchPath, writerOptions); } CompareArchivesByPath(unmodified, scratchPath, Encoding.GetEncoding(866)); Directory.Delete(SCRATCH_FILES_PATH, true); @@ -395,7 +395,7 @@ namespace SharpCompress.Test.Zip } [Fact] - public void Zip_Random_Entry_Access() + public async Task Zip_Random_Entry_Access() { string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); @@ -421,8 +421,9 @@ namespace SharpCompress.Test.Zip if (count2 == count) { var s = e.OpenEntryStream(); - s.ReadByte(); //Actually access stream - s.Dispose(); + byte[] b = new byte[1]; + await s.ReadAsync(b, 0, 1); //Actually access stream + await s.DisposeAsync(); break; } } @@ -497,26 +498,26 @@ namespace SharpCompress.Test.Zip await using (IWriter zipWriter = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate)) { - zipWriter.Write("foo.txt", new MemoryStream(new byte[0])); - zipWriter.Write("foo2.txt", new MemoryStream(new byte[10])); + await zipWriter.WriteAsync("foo.txt", new MemoryStream(new byte[0])); + await zipWriter.WriteAsync("foo2.txt", new MemoryStream(new byte[10])); } stream = new MemoryStream(stream.ToArray()); - File.WriteAllBytes(Path.Combine(SCRATCH_FILES_PATH, "foo.zip"), stream.ToArray()); + await File.WriteAllBytesAsync(Path.Combine(SCRATCH_FILES_PATH, "foo.zip"), stream.ToArray()); using (var zipArchive = ZipArchive.Open(stream)) { foreach (var entry in zipArchive.Entries) { - using (var entryStream = entry.OpenEntryStream()) + await using (var entryStream = entry.OpenEntryStream()) { MemoryStream tempStream = new MemoryStream(); const int bufSize = 0x1000; byte[] buf = new byte[bufSize]; int bytesRead = 0; - while ((bytesRead = entryStream.Read(buf, 0, bufSize)) > 0) + while ((bytesRead = await entryStream.ReadAsync(buf, 0, bufSize)) > 0) { - tempStream.Write(buf, 0, bytesRead); + await tempStream.WriteAsync(buf, 0, bytesRead); } } } @@ -524,13 +525,13 @@ namespace SharpCompress.Test.Zip } [Fact] - public void Zip_BadLocalExtra_Read() + public async Task Zip_BadLocalExtra_Read() { string zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.badlocalextra.zip"); using (ZipArchive za = ZipArchive.Open(zipPath)) { - var ex = Record.Exception(() => + var ex = await Record.ExceptionAsync(async () => { var firstEntry = za.Entries.First(x => x.Key == "first.txt"); var buffer = new byte[4096]; @@ -538,7 +539,7 @@ namespace SharpCompress.Test.Zip using (var memoryStream = new MemoryStream()) using (var firstStream = firstEntry.OpenEntryStream()) { - firstStream.CopyTo(memoryStream); + await firstStream.CopyToAsync(memoryStream); Assert.Equal(199, memoryStream.Length); } }); diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index cb4d1f4e..0b16ff2d 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -279,25 +279,25 @@ namespace SharpCompress.Test.Zip new Tuple("foo2.txt", new byte[10]) }; - using (var memory = new MemoryStream()) + await using (var memory = new MemoryStream()) { Stream stream = new TestStream(memory, read: true, write: true, seek: false); await using (IWriter zipWriter = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate)) { - zipWriter.Write(expected[0].Item1, new MemoryStream(expected[0].Item2)); - zipWriter.Write(expected[1].Item1, new MemoryStream(expected[1].Item2)); + await zipWriter.WriteAsync(expected[0].Item1, new MemoryStream(expected[0].Item2)); + await zipWriter.WriteAsync(expected[1].Item1, new MemoryStream(expected[1].Item2)); } stream = new MemoryStream(memory.ToArray()); - File.WriteAllBytes(Path.Combine(SCRATCH_FILES_PATH, "foo.zip"), memory.ToArray()); + await File.WriteAllBytesAsync(Path.Combine(SCRATCH_FILES_PATH, "foo.zip"), memory.ToArray()); using (IReader zipReader = ZipReader.Open(new NonDisposingStream(stream, true))) { var i = 0; while (zipReader.MoveToNextEntry()) { - using (EntryStream entry = zipReader.OpenEntryStream()) + await using (EntryStream entry = zipReader.OpenEntryStream()) { MemoryStream tempStream = new MemoryStream(); const int bufSize = 0x1000;