diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs index a9adf6f5..11ccb114 100644 --- a/src/SharpCompress/Archives/AbstractArchive.cs +++ b/src/SharpCompress/Archives/AbstractArchive.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Readers; @@ -19,7 +20,7 @@ namespace SharpCompress.Archives private bool disposed; - internal AbstractArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerOptions) + internal AbstractArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerOptions, CancellationToken cancellationToken) { Type = type; if (!fileInfo.Exists) @@ -28,19 +29,19 @@ namespace SharpCompress.Archives } ReaderOptions = readerOptions; readerOptions.LeaveStreamOpen = false; - lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(fileInfo)); - lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes)); + lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(fileInfo, cancellationToken)); + lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes, cancellationToken)); } - protected abstract IAsyncEnumerable LoadVolumes(FileInfo file); + protected abstract IAsyncEnumerable LoadVolumes(FileInfo file, CancellationToken cancellationToken); - internal AbstractArchive(ArchiveType type, IAsyncEnumerable streams, ReaderOptions readerOptions) + internal AbstractArchive(ArchiveType type, IAsyncEnumerable streams, ReaderOptions readerOptions, CancellationToken cancellationToken) { Type = type; ReaderOptions = readerOptions; - lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(streams.Select(CheckStreams))); - lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes)); + lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(streams.Select(CheckStreams), cancellationToken)); + lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes, cancellationToken)); } internal AbstractArchive(ArchiveType type) @@ -89,8 +90,8 @@ namespace SharpCompress.Archives return await Entries.AggregateAsync(0L, (total, cf) => total + cf.Size); } - protected abstract IAsyncEnumerable LoadVolumes(IAsyncEnumerable streams); - protected abstract IAsyncEnumerable LoadEntries(IAsyncEnumerable volumes); + protected abstract IAsyncEnumerable LoadVolumes(IAsyncEnumerable streams, CancellationToken cancellationToken); + protected abstract IAsyncEnumerable LoadEntries(IAsyncEnumerable volumes, CancellationToken cancellationToken); IAsyncEnumerable IArchive.Entries => Entries.Select(x => (IArchiveEntry)x); diff --git a/src/SharpCompress/Archives/AbstractWritableArchive.cs b/src/SharpCompress/Archives/AbstractWritableArchive.cs index b4dadeca..6c8d7da2 100644 --- a/src/SharpCompress/Archives/AbstractWritableArchive.cs +++ b/src/SharpCompress/Archives/AbstractWritableArchive.cs @@ -42,13 +42,15 @@ namespace SharpCompress.Archives { } - internal AbstractWritableArchive(ArchiveType type, Stream stream, ReaderOptions readerFactoryOptions) - : base(type, stream.AsAsyncEnumerable(), readerFactoryOptions) + internal AbstractWritableArchive(ArchiveType type, Stream stream, ReaderOptions readerFactoryOptions, + CancellationToken cancellationToken) + : base(type, stream.AsAsyncEnumerable(), readerFactoryOptions, cancellationToken) { } - internal AbstractWritableArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerFactoryOptions) - : base(type, fileInfo, readerFactoryOptions) + internal AbstractWritableArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerFactoryOptions, + CancellationToken cancellationToken) + : base(type, fileInfo, readerFactoryOptions, cancellationToken) { } diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index ac79145c..4a09581a 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using SharpCompress.Archives.GZip; //using SharpCompress.Archives.Rar; //using SharpCompress.Archives.SevenZip; -//using SharpCompress.Archives.Tar; +using SharpCompress.Archives.Tar; using SharpCompress.Archives.Zip; using SharpCompress.Common; using SharpCompress.Readers; @@ -51,12 +51,12 @@ namespace SharpCompress.Archives stream.Seek(0, SeekOrigin.Begin); return RarArchive.Open(stream, readerOptions); } - stream.Seek(0, SeekOrigin.Begin); - if (TarArchive.IsTarFile(stream)) + stream.Seek(0, SeekOrigin.Begin); */ + if (await TarArchive.IsTarFileAsync(stream, cancellationToken)) { stream.Seek(0, SeekOrigin.Begin); return TarArchive.Open(stream, readerOptions); - } */ + } throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip, LZip"); } diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs index a11ce60a..0977d17c 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchive.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using SharpCompress.Common; @@ -32,10 +33,11 @@ namespace SharpCompress.Archives.GZip /// /// /// - public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default) { fileInfo.CheckNotNull(nameof(fileInfo)); - return new GZipArchive(fileInfo, readerOptions ?? new ReaderOptions()); + return new GZipArchive(fileInfo, readerOptions ?? new ReaderOptions(), cancellationToken); } /// @@ -43,10 +45,11 @@ namespace SharpCompress.Archives.GZip /// /// /// - public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) + public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default) { stream.CheckNotNull(nameof(stream)); - return new GZipArchive(stream, readerOptions ?? new ReaderOptions()); + return new GZipArchive(stream, readerOptions ?? new ReaderOptions(), cancellationToken); } public static GZipArchive Create() @@ -59,12 +62,14 @@ namespace SharpCompress.Archives.GZip /// /// /// - internal GZipArchive(FileInfo fileInfo, ReaderOptions options) - : base(ArchiveType.GZip, fileInfo, options) + internal GZipArchive(FileInfo fileInfo, ReaderOptions options, + CancellationToken cancellationToken) + : base(ArchiveType.GZip, fileInfo, options, cancellationToken) { } - protected override IAsyncEnumerable LoadVolumes(FileInfo file) + protected override IAsyncEnumerable LoadVolumes(FileInfo file, + CancellationToken cancellationToken) { return new GZipVolume(file, ReaderOptions).AsAsyncEnumerable(); } @@ -102,7 +107,7 @@ namespace SharpCompress.Archives.GZip using var header = MemoryPool.Shared.Rent(10); // workitem 8501: handle edge case (decompress empty stream) - if (!await stream.ReadFullyAsync(header.Memory, cancellationToken)) + if (!await stream.ReadFullyAsync(header.Memory.Slice(0, 10), cancellationToken)) { return false; } @@ -120,8 +125,9 @@ namespace SharpCompress.Archives.GZip /// /// /// - internal GZipArchive(Stream stream, ReaderOptions options) - : base(ArchiveType.GZip, stream, options) + internal GZipArchive(Stream stream, ReaderOptions options, + CancellationToken cancellationToken) + : base(ArchiveType.GZip, stream, options, cancellationToken) { } @@ -160,15 +166,19 @@ namespace SharpCompress.Archives.GZip } } - protected override async IAsyncEnumerable LoadVolumes(IAsyncEnumerable streams) + protected override async IAsyncEnumerable LoadVolumes(IAsyncEnumerable streams, + [EnumeratorCancellation]CancellationToken cancellationToken) { - yield return new GZipVolume(await streams.FirstAsync(), ReaderOptions); + yield return new GZipVolume(await streams.FirstAsync(cancellationToken: cancellationToken), ReaderOptions); } - protected override async IAsyncEnumerable LoadEntries(IAsyncEnumerable volumes) + protected override async IAsyncEnumerable LoadEntries(IAsyncEnumerable volumes, + [EnumeratorCancellation]CancellationToken cancellationToken) { - Stream stream = (await volumes.SingleAsync()).Stream; - yield return new GZipArchiveEntry(this, new GZipFilePart(stream, ReaderOptions.ArchiveEncoding)); + Stream stream = (await volumes.SingleAsync(cancellationToken: cancellationToken)).Stream; + var part = new GZipFilePart(ReaderOptions.ArchiveEncoding); + await part.Initialize(stream, cancellationToken); + yield return new GZipArchiveEntry(this, part); } protected override async ValueTask CreateReaderForSolidExtraction() diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 51697568..1b91685d 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using SharpCompress.Common; @@ -33,10 +34,11 @@ namespace SharpCompress.Archives.Tar /// /// /// - public static TarArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + public static TarArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default) { fileInfo.CheckNotNull(nameof(fileInfo)); - return new TarArchive(fileInfo, readerOptions ?? new ReaderOptions()); + return new TarArchive(fileInfo, readerOptions ?? new ReaderOptions(), cancellationToken); } /// @@ -44,34 +46,35 @@ namespace SharpCompress.Archives.Tar /// /// /// - public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null) + public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default) { stream.CheckNotNull(nameof(stream)); - return new TarArchive(stream, readerOptions ?? new ReaderOptions()); + return new TarArchive(stream, readerOptions ?? new ReaderOptions(), cancellationToken); } - public static bool IsTarFile(string filePath) + public static ValueTask IsTarFileAsync(string filePath, CancellationToken cancellationToken = default) { - return IsTarFile(new FileInfo(filePath)); + return IsTarFileAsync(new FileInfo(filePath), cancellationToken); } - public static bool IsTarFile(FileInfo fileInfo) + public static async ValueTask IsTarFileAsync(FileInfo fileInfo, CancellationToken cancellationToken = default) { if (!fileInfo.Exists) { return false; } - using Stream stream = fileInfo.OpenRead(); - return IsTarFile(stream); + await using Stream stream = fileInfo.OpenRead(); + return await IsTarFileAsync(stream, cancellationToken); } - public static bool IsTarFile(Stream stream) + public static async ValueTask IsTarFileAsync(Stream stream, CancellationToken cancellationToken = default) { try { - TarHeader tarHeader = new TarHeader(new ArchiveEncoding()); - bool readSucceeded = tarHeader.Read(new BinaryReader(stream)); + TarHeader tarHeader = new(new ArchiveEncoding()); + bool readSucceeded = await tarHeader.Read(stream, cancellationToken); bool isEmptyArchive = tarHeader.Name.Length == 0 && tarHeader.Size == 0 && Enum.IsDefined(typeof(EntryType), tarHeader.EntryType); return readSucceeded || isEmptyArchive; } @@ -86,14 +89,15 @@ namespace SharpCompress.Archives.Tar /// /// /// - internal TarArchive(FileInfo fileInfo, ReaderOptions readerOptions) - : base(ArchiveType.Tar, fileInfo, readerOptions) + internal TarArchive(FileInfo fileInfo, ReaderOptions readerOptions, + CancellationToken cancellationToken) + : base(ArchiveType.Tar, fileInfo, readerOptions, cancellationToken) { } - protected override IEnumerable LoadVolumes(FileInfo file) + protected override IAsyncEnumerable LoadVolumes(FileInfo file, CancellationToken cancellationToken) { - return new TarVolume(file.OpenRead(), ReaderOptions).AsEnumerable(); + return new TarVolume(file.OpenRead(), ReaderOptions).AsAsyncEnumerable(); } /// @@ -101,8 +105,9 @@ namespace SharpCompress.Archives.Tar /// /// /// - internal TarArchive(Stream stream, ReaderOptions readerOptions) - : base(ArchiveType.Tar, stream, readerOptions) + internal TarArchive(Stream stream, ReaderOptions readerOptions, + CancellationToken cancellationToken) + : base(ArchiveType.Tar, stream, readerOptions, cancellationToken) { } @@ -111,16 +116,18 @@ namespace SharpCompress.Archives.Tar { } - protected override IEnumerable LoadVolumes(IEnumerable streams) + protected override async IAsyncEnumerable LoadVolumes(IAsyncEnumerable streams, + [EnumeratorCancellation]CancellationToken cancellationToken) { - return new TarVolume(streams.First(), ReaderOptions).AsEnumerable(); + yield return new TarVolume(await streams.FirstAsync(cancellationToken: cancellationToken), ReaderOptions); } - protected override IEnumerable LoadEntries(IEnumerable volumes) + protected override async IAsyncEnumerable LoadEntries(IAsyncEnumerable volumes, + [EnumeratorCancellation]CancellationToken cancellationToken) { - Stream stream = volumes.Single().Stream; + Stream stream = (await volumes.SingleAsync(cancellationToken: cancellationToken)).Stream; TarHeader? previousHeader = null; - foreach (TarHeader? header in TarHeaderFactory.ReadHeader(StreamingMode.Seekable, stream, ReaderOptions.ArchiveEncoding)) + await foreach (TarHeader? header in TarHeaderFactory.ReadHeader(StreamingMode.Seekable, stream, ReaderOptions.ArchiveEncoding, cancellationToken)) { if (header != null) { @@ -161,35 +168,37 @@ namespace SharpCompress.Archives.Tar public static TarArchive Create() { - return new TarArchive(); + return new(); } - protected override TarArchiveEntry CreateEntryInternal(string filePath, Stream source, - long size, DateTime? modified, bool closeStream) + protected override ValueTask CreateEntryInternal(string filePath, Stream source, + long size, DateTime? modified, bool closeStream, + CancellationToken cancellationToken) { - return new TarWritableArchiveEntry(this, source, CompressionType.Unknown, filePath, size, modified, - closeStream); + return new (new TarWritableArchiveEntry(this, source, CompressionType.Unknown, filePath, size, modified, + closeStream)); } - protected override async Task SaveToAsync(Stream stream, WriterOptions options, - IEnumerable oldEntries, - IEnumerable newEntries, - CancellationToken cancellationToken = default) + protected override async ValueTask SaveToAsync(Stream stream, WriterOptions options, + IAsyncEnumerable oldEntries, + IAsyncEnumerable newEntries, + CancellationToken cancellationToken = default) { await using var writer = new TarWriter(stream, new TarWriterOptions(options)); - foreach (var entry in oldEntries.Concat(newEntries) - .Where(x => !x.IsDirectory)) + await foreach (var entry in oldEntries.Concat(newEntries) + .Where(x => !x.IsDirectory) + .WithCancellation(cancellationToken)) { await using var entryStream = entry.OpenEntryStream(); await writer.WriteAsync(entry.Key, entryStream, entry.LastModifiedTime, cancellationToken); } } - protected override IReader CreateReaderForSolidExtraction() + protected override async ValueTask CreateReaderForSolidExtraction() { - var stream = Volumes.Single().Stream; + var stream = (await Volumes.SingleAsync()).Stream; stream.Position = 0; - return TarReader.Open(stream); + return await TarReader.OpenAsync(stream); } } } diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index 37a653a0..c7e861d6 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using SharpCompress.Common; @@ -43,10 +44,11 @@ namespace SharpCompress.Archives.Zip /// /// /// - public static ZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + public static ZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default) { fileInfo.CheckNotNull(nameof(fileInfo)); - return new ZipArchive(fileInfo, readerOptions ?? new ReaderOptions()); + return new ZipArchive(fileInfo, readerOptions ?? new ReaderOptions(), cancellationToken); } /// @@ -54,10 +56,11 @@ namespace SharpCompress.Archives.Zip /// /// /// - public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) + public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default) { stream.CheckNotNull(nameof(stream)); - return new ZipArchive(stream, readerOptions ?? new ReaderOptions()); + return new ZipArchive(stream, readerOptions ?? new ReaderOptions(), cancellationToken); } public static ValueTask IsZipFile(string filePath, string? password = null) @@ -112,13 +115,15 @@ namespace SharpCompress.Archives.Zip /// /// /// - internal ZipArchive(FileInfo fileInfo, ReaderOptions readerOptions) - : base(ArchiveType.Zip, fileInfo, readerOptions) + internal ZipArchive(FileInfo fileInfo, ReaderOptions readerOptions, + CancellationToken cancellationToken) + : base(ArchiveType.Zip, fileInfo, readerOptions, cancellationToken) { headerFactory = new SeekableZipHeaderFactory(readerOptions.Password, readerOptions.ArchiveEncoding); } - protected override IAsyncEnumerable LoadVolumes(FileInfo file) + protected override IAsyncEnumerable LoadVolumes(FileInfo file, + CancellationToken cancellationToken) { return new ZipVolume(file.OpenRead(), ReaderOptions).AsAsyncEnumerable(); } @@ -133,21 +138,24 @@ namespace SharpCompress.Archives.Zip /// /// /// - internal ZipArchive(Stream stream, ReaderOptions readerOptions) - : base(ArchiveType.Zip, stream, readerOptions) + internal ZipArchive(Stream stream, ReaderOptions readerOptions, + CancellationToken cancellationToken) + : base(ArchiveType.Zip, stream, readerOptions, cancellationToken) { headerFactory = new SeekableZipHeaderFactory(readerOptions.Password, readerOptions.ArchiveEncoding); } - protected override async IAsyncEnumerable LoadVolumes(IAsyncEnumerable streams) + protected override async IAsyncEnumerable LoadVolumes(IAsyncEnumerable streams, + [EnumeratorCancellation]CancellationToken cancellationToken) { - yield return new ZipVolume(await streams.FirstAsync(), ReaderOptions); + yield return new ZipVolume(await streams.FirstAsync(cancellationToken: cancellationToken), ReaderOptions); } - protected override async IAsyncEnumerable LoadEntries(IAsyncEnumerable volumes) + protected override async IAsyncEnumerable LoadEntries(IAsyncEnumerable volumes, + [EnumeratorCancellation]CancellationToken cancellationToken) { await Task.CompletedTask; - var volume = await volumes.SingleAsync(); + var volume = await volumes.SingleAsync(cancellationToken: cancellationToken); Stream stream = volume.Stream; foreach (ZipHeader h in headerFactory.ReadSeekableHeader(stream)) { diff --git a/src/SharpCompress/Common/EntryStream.cs b/src/SharpCompress/Common/EntryStream.cs index 43ca0537..414f5a06 100644 --- a/src/SharpCompress/Common/EntryStream.cs +++ b/src/SharpCompress/Common/EntryStream.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Readers; namespace SharpCompress.Common @@ -20,25 +22,29 @@ namespace SharpCompress.Common /// /// When reading a stream from OpenEntryStream, the stream must be completed so use this to finish reading the entire entry. /// - public void SkipEntry() + public async ValueTask SkipEntryAsync(CancellationToken cancellationToken = default) { - this.Skip(); + await this.SkipAsync(cancellationToken); _completed = true; } - protected override void Dispose(bool disposing) + public override async ValueTask DisposeAsync() { if (!(_completed || _reader.Cancelled)) { - SkipEntry(); + await SkipEntryAsync(); } if (_isDisposed) { return; } _isDisposed = true; - base.Dispose(disposing); - _stream.Dispose(); + await _stream.DisposeAsync(); + } + + protected override void Dispose(bool disposing) + { + throw new NotImplementedException(); } public override bool CanRead => true; @@ -55,9 +61,9 @@ namespace SharpCompress.Common public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } - public override int Read(byte[] buffer, int offset, int count) + public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { - int read = _stream.Read(buffer, offset, count); + int read = await _stream.ReadAsync(buffer, cancellationToken); if (read <= 0) { _completed = true; @@ -65,14 +71,14 @@ namespace SharpCompress.Common return read; } + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + public override int ReadByte() { - int value = _stream.ReadByte(); - if (value == -1) - { - _completed = true; - } - return value; + throw new NotImplementedException(); } public override long Seek(long offset, SeekOrigin origin) diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index c4f62000..1d135bad 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; using System.Threading.Tasks; namespace SharpCompress.Common.GZip @@ -42,10 +44,12 @@ namespace SharpCompress.Common.GZip internal override IEnumerable Parts => _filePart.AsEnumerable(); - internal static async IAsyncEnumerable GetEntries(Stream stream, OptionsBase options) + internal static async IAsyncEnumerable GetEntries(Stream stream, OptionsBase options, + [EnumeratorCancellation] CancellationToken cancellationToken) { - await Task.CompletedTask; - yield return new GZipEntry(new GZipFilePart(stream, options.ArchiveEncoding)); + var part = new GZipFilePart(options.ArchiveEncoding); + await part.Initialize(stream, cancellationToken); + yield return new GZipEntry(part); } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Tar/TarEntry.cs b/src/SharpCompress/Common/Tar/TarEntry.cs index dc8cc7ab..f1239f8a 100644 --- a/src/SharpCompress/Common/Tar/TarEntry.cs +++ b/src/SharpCompress/Common/Tar/TarEntry.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; using SharpCompress.Common.Tar.Headers; using SharpCompress.IO; @@ -46,10 +48,11 @@ namespace SharpCompress.Common.Tar internal override IEnumerable Parts => _filePart.AsEnumerable(); - internal static IEnumerable GetEntries(StreamingMode mode, Stream stream, - CompressionType compressionType, ArchiveEncoding archiveEncoding) + internal static async IAsyncEnumerable GetEntries(StreamingMode mode, Stream stream, + CompressionType compressionType, ArchiveEncoding archiveEncoding, + [EnumeratorCancellation]CancellationToken cancellationToken) { - foreach (TarHeader h in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding)) + await foreach (TarHeader h in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding, cancellationToken)) { if (h != null) { diff --git a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs index eb4d7464..1ed234bb 100644 --- a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs +++ b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; using SharpCompress.Common.Tar.Headers; using SharpCompress.IO; @@ -7,17 +9,17 @@ namespace SharpCompress.Common.Tar { internal static class TarHeaderFactory { - internal static IEnumerable ReadHeader(StreamingMode mode, Stream stream, ArchiveEncoding archiveEncoding) + internal static async IAsyncEnumerable ReadHeader(StreamingMode mode, Stream stream, ArchiveEncoding archiveEncoding, + [EnumeratorCancellation]CancellationToken cancellationToken) { while (true) { TarHeader? header = null; try { - BinaryReader reader = new BinaryReader(stream); header = new TarHeader(archiveEncoding); - if (!header.Read(reader)) + if (!await header.Read(stream, cancellationToken)) { yield break; } @@ -25,10 +27,10 @@ namespace SharpCompress.Common.Tar { case StreamingMode.Seekable: { - header.DataStartPosition = reader.BaseStream.Position; + header.DataStartPosition = stream.Position; //skip to nearest 512 - reader.BaseStream.Position += PadTo512(header.Size); + stream.Position += PadTo512(header.Size); } break; case StreamingMode.Streaming: diff --git a/src/SharpCompress/IO/NonDisposingStream.cs b/src/SharpCompress/IO/NonDisposingStream.cs index dfb8f80e..ceb041e4 100644 --- a/src/SharpCompress/IO/NonDisposingStream.cs +++ b/src/SharpCompress/IO/NonDisposingStream.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace SharpCompress.IO { @@ -13,6 +15,16 @@ namespace SharpCompress.IO public bool ThrowOnDispose { get; set; } + public override ValueTask DisposeAsync() + { + if (ThrowOnDispose) + { + throw new InvalidOperationException($"Attempt to dispose of a {nameof(NonDisposingStream)} when {nameof(ThrowOnDispose)} is {ThrowOnDispose}"); + } + + return base.DisposeAsync(); + } + protected override void Dispose(bool disposing) { if (ThrowOnDispose) @@ -40,7 +52,17 @@ namespace SharpCompress.IO public override int Read(byte[] buffer, int offset, int count) { - return Stream.Read(buffer, offset, count); + throw new NotImplementedException(); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = new CancellationToken()) + { + return Stream.ReadAsync(buffer, cancellationToken); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return Stream.ReadAsync(buffer, offset, count, cancellationToken); } public override long Seek(long offset, SeekOrigin origin) @@ -55,7 +77,17 @@ namespace SharpCompress.IO public override void Write(byte[] buffer, int offset, int count) { - Stream.Write(buffer, offset, count); + throw new NotImplementedException(); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = new CancellationToken()) + { + return Stream.WriteAsync(buffer, cancellationToken); } #if !NET461 && !NETSTANDARD2_0 diff --git a/src/SharpCompress/Readers/AbstractReader.cs b/src/SharpCompress/Readers/AbstractReader.cs index 99dc7f8d..26be6047 100644 --- a/src/SharpCompress/Readers/AbstractReader.cs +++ b/src/SharpCompress/Readers/AbstractReader.cs @@ -81,11 +81,12 @@ namespace SharpCompress.Readers } if (entriesForCurrentReadStream is null) { - return await LoadStreamForReading(RequestInitialStream()); + //TODO: real token? + return await LoadStreamForReading(RequestInitialStream(), CancellationToken.None); } if (!wroteCurrentEntry) { - SkipEntry(); + await SkipEntry(CancellationToken.None); } wroteCurrentEntry = false; if (await NextEntryForCurrentStream()) @@ -96,7 +97,7 @@ namespace SharpCompress.Readers return false; } - protected async Task LoadStreamForReading(Stream stream) + protected async Task LoadStreamForReading(Stream stream, CancellationToken cancellationToken) { await (entriesForCurrentReadStream?.DisposeAsync() ?? new ValueTask(Task.CompletedTask)); if (stream is null || !stream.CanRead) @@ -105,7 +106,7 @@ namespace SharpCompress.Readers + (Entry?.Key ?? "unknown") + "'. A new readable stream is required. Use Cancel if it was intended."); } - entriesForCurrentReadStream = GetEntries(stream).GetAsyncEnumerator(); + entriesForCurrentReadStream = GetEntries(stream, cancellationToken).GetAsyncEnumerator(cancellationToken); return await (entriesForCurrentReadStream?.MoveNextAsync() ?? new ValueTask(Task.FromResult(false))); } @@ -119,19 +120,19 @@ namespace SharpCompress.Readers return await (entriesForCurrentReadStream?.MoveNextAsync() ?? new ValueTask(Task.FromResult(false))); } - protected abstract IAsyncEnumerable GetEntries(Stream stream); + protected abstract IAsyncEnumerable GetEntries(Stream stream, CancellationToken cancellationToken); #region Entry Skip/Write - private void SkipEntry() + private async ValueTask SkipEntry(CancellationToken cancellationToken) { if (Entry?.IsDirectory == true) { - Skip(); + await SkipAsync(cancellationToken); } } - private void Skip() + private async ValueTask SkipAsync(CancellationToken cancellationToken) { if (Entry is null) { @@ -148,15 +149,15 @@ namespace SharpCompress.Readers if (rawStream != null) { var bytesToAdvance = Entry.CompressedSize; - rawStream.Skip(bytesToAdvance); + await rawStream.SkipAsync(bytesToAdvance, cancellationToken: cancellationToken); part.Skipped = true; return; } } //don't know the size so we have to try to decompress to skip - using (var s = OpenEntryStream()) + await using (var s = OpenEntryStream()) { - s.Skip(); + await s.SkipAsync(cancellationToken); } } diff --git a/src/SharpCompress/Readers/GZip/GZipReader.cs b/src/SharpCompress/Readers/GZip/GZipReader.cs index a66c0ea5..951c654e 100644 --- a/src/SharpCompress/Readers/GZip/GZipReader.cs +++ b/src/SharpCompress/Readers/GZip/GZipReader.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Threading; using SharpCompress.Common; using SharpCompress.Common.GZip; @@ -31,9 +32,9 @@ namespace SharpCompress.Readers.GZip #endregion Open - protected override IAsyncEnumerable GetEntries(Stream stream) + protected override IAsyncEnumerable GetEntries(Stream stream, CancellationToken cancellationToken) { - return GZipEntry.GetEntries(stream, Options); + return GZipEntry.GetEntries(stream, Options, cancellationToken); } } } diff --git a/src/SharpCompress/Readers/ReaderFactory.cs b/src/SharpCompress/Readers/ReaderFactory.cs index 9983b5be..3c3c0319 100644 --- a/src/SharpCompress/Readers/ReaderFactory.cs +++ b/src/SharpCompress/Readers/ReaderFactory.cs @@ -3,7 +3,7 @@ using System.IO; using System.Threading.Tasks; using SharpCompress.Archives.GZip; //using SharpCompress.Archives.Rar; -//using SharpCompress.Archives.Tar; +using SharpCompress.Archives.Tar; using SharpCompress.Archives.Zip; using SharpCompress.Common; using SharpCompress.Compressors; @@ -12,7 +12,7 @@ using SharpCompress.Compressors.Deflate; using SharpCompress.IO; using SharpCompress.Readers.GZip; //using SharpCompress.Readers.Rar; -//using SharpCompress.Readers.Tar; +using SharpCompress.Readers.Tar; using SharpCompress.Readers.Zip; using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.Xz; @@ -45,12 +45,12 @@ namespace SharpCompress.Readers if (await GZipArchive.IsGZipFileAsync(rewindableStream)) { rewindableStream.Rewind(false); - /*GZipStream testStream = new GZipStream(rewindableStream, CompressionMode.Decompress); - if (TarArchive.IsTarFile(testStream)) + GZipStream testStream = new GZipStream(rewindableStream, CompressionMode.Decompress); + if (await TarArchive.IsTarFileAsync(testStream)) { rewindableStream.Rewind(true); return new TarReader(rewindableStream, options, CompressionType.GZip); - } */ + } rewindableStream.Rewind(true); return GZipReader.Open(rewindableStream, options); } @@ -59,38 +59,38 @@ namespace SharpCompress.Readers if (BZip2Stream.IsBZip2(rewindableStream)) { rewindableStream.Rewind(false); - /*BZip2Stream testStream = new BZip2Stream(new NonDisposingStream(rewindableStream), CompressionMode.Decompress, false); - if (TarArchive.IsTarFile(testStream)) + BZip2Stream testStream = new BZip2Stream(new NonDisposingStream(rewindableStream), CompressionMode.Decompress, false); + if (await TarArchive.IsTarFileAsync(testStream)) { rewindableStream.Rewind(true); return new TarReader(rewindableStream, options, CompressionType.BZip2); - } */ + } } rewindableStream.Rewind(false); if (LZipStream.IsLZipFile(rewindableStream)) { rewindableStream.Rewind(false); - /* LZipStream testStream = new LZipStream(new NonDisposingStream(rewindableStream), CompressionMode.Decompress); - if (TarArchive.IsTarFile(testStream)) + LZipStream testStream = new LZipStream(new NonDisposingStream(rewindableStream), CompressionMode.Decompress); + if (await TarArchive.IsTarFileAsync(testStream)) { rewindableStream.Rewind(true); return new TarReader(rewindableStream, options, CompressionType.LZip); - } */ + } } rewindableStream.Rewind(false); /* if (RarArchive.IsRarFile(rewindableStream, options)) { rewindableStream.Rewind(true); return RarReader.Open(rewindableStream, options); - } + } */ rewindableStream.Rewind(false); - if (TarArchive.IsTarFile(rewindableStream)) + if (await TarArchive.IsTarFileAsync(rewindableStream)) { rewindableStream.Rewind(true); - return TarReader.Open(rewindableStream, options); - } */ + return await TarReader.OpenAsync(rewindableStream, options); + } rewindableStream.Rewind(false); if (XZStream.IsXZStream(rewindableStream)) { diff --git a/src/SharpCompress/Readers/Tar/TarReader.cs b/src/SharpCompress/Readers/Tar/TarReader.cs index a2b7ecc7..b14b89fc 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Archives.GZip; using SharpCompress.Archives.Tar; using SharpCompress.Common; @@ -67,17 +69,17 @@ namespace SharpCompress.Readers.Tar /// /// /// - public static TarReader Open(Stream stream, ReaderOptions? options = null) + public static async ValueTask OpenAsync(Stream stream, ReaderOptions? options = null) { stream.CheckNotNull(nameof(stream)); options = options ?? new ReaderOptions(); RewindableStream rewindableStream = new RewindableStream(stream); rewindableStream.StartRecording(); - if (GZipArchive.IsGZipFile(rewindableStream)) + if (await GZipArchive.IsGZipFileAsync(rewindableStream)) { rewindableStream.Rewind(false); GZipStream testStream = new GZipStream(rewindableStream, CompressionMode.Decompress); - if (TarArchive.IsTarFile(testStream)) + if (await TarArchive.IsTarFileAsync(testStream)) { rewindableStream.Rewind(true); return new TarReader(rewindableStream, options, CompressionType.GZip); @@ -90,7 +92,7 @@ namespace SharpCompress.Readers.Tar { rewindableStream.Rewind(false); BZip2Stream testStream = new BZip2Stream(rewindableStream, CompressionMode.Decompress, false); - if (TarArchive.IsTarFile(testStream)) + if (await TarArchive.IsTarFileAsync(testStream)) { rewindableStream.Rewind(true); return new TarReader(rewindableStream, options, CompressionType.BZip2); @@ -103,7 +105,7 @@ namespace SharpCompress.Readers.Tar { rewindableStream.Rewind(false); LZipStream testStream = new LZipStream(rewindableStream, CompressionMode.Decompress); - if (TarArchive.IsTarFile(testStream)) + if (await TarArchive.IsTarFileAsync(testStream)) { rewindableStream.Rewind(true); return new TarReader(rewindableStream, options, CompressionType.LZip); @@ -116,9 +118,9 @@ namespace SharpCompress.Readers.Tar #endregion Open - protected override IEnumerable GetEntries(Stream stream) + protected override IAsyncEnumerable GetEntries(Stream stream, CancellationToken cancellationToken) { - return TarEntry.GetEntries(StreamingMode.Streaming, stream, compressionType, Options.ArchiveEncoding); + return TarEntry.GetEntries(StreamingMode.Streaming, stream, compressionType, Options.ArchiveEncoding, cancellationToken); } } } diff --git a/src/SharpCompress/Readers/Zip/ZipReader.cs b/src/SharpCompress/Readers/Zip/ZipReader.cs index 5ee7f4fe..31853706 100644 --- a/src/SharpCompress/Readers/Zip/ZipReader.cs +++ b/src/SharpCompress/Readers/Zip/ZipReader.cs @@ -1,5 +1,8 @@ using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.Zip; using SharpCompress.Common.Zip.Headers; @@ -35,9 +38,9 @@ namespace SharpCompress.Readers.Zip #endregion Open - protected override async IAsyncEnumerable GetEntries(Stream stream) + protected override async IAsyncEnumerable GetEntries(Stream stream, [EnumeratorCancellation] CancellationToken cancellationToken) { - await foreach (ZipHeader h in _headerFactory.ReadStreamHeader(stream)) + await foreach (ZipHeader h in _headerFactory.ReadStreamHeader(stream).WithCancellation(cancellationToken)) { if (h != null) { diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index d974ec74..da6a9310 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -38,26 +38,16 @@ - - - - - - - - - - diff --git a/src/SharpCompress/Writers/WriterFactory.cs b/src/SharpCompress/Writers/WriterFactory.cs index e32e3ec7..7efa93d0 100644 --- a/src/SharpCompress/Writers/WriterFactory.cs +++ b/src/SharpCompress/Writers/WriterFactory.cs @@ -2,7 +2,7 @@ using System.IO; using SharpCompress.Common; using SharpCompress.Writers.GZip; -//using SharpCompress.Writers.Tar; +using SharpCompress.Writers.Tar; using SharpCompress.Writers.Zip; namespace SharpCompress.Writers @@ -27,8 +27,7 @@ namespace SharpCompress.Writers } case ArchiveType.Tar: { - throw new NotImplementedException(); - //return new TarWriter(stream, new TarWriterOptions(writerOptions)); + return new TarWriter(stream, new TarWriterOptions(writerOptions)); } default: {