mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Preserve async disposal across all supported frameworks
This commit is contained in:
@@ -694,6 +694,8 @@ using (var archive = ZipArchive.OpenArchive("archive.zip", options))
|
||||
|
||||
### Async Extract with Cancellation
|
||||
|
||||
Async archive and reader/writer operations dispose SharpCompress resources asynchronously on every target, including .NET Framework 4.8 and .NET Standard 2.0. `SharpCompressStream.DisposeAsync()` and `ZlibStream.DisposeAsync()` are available on every target; stream wrappers forward disposal to runtime `IAsyncDisposable` implementations where available, falling back to synchronous disposal only for streams that do not support it.
|
||||
|
||||
```csharp
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromMinutes(5));
|
||||
|
||||
@@ -25,10 +25,11 @@ public partial class GZipArchive
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
#if SYNC_ONLY
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
#else
|
||||
await using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
await using var scope = stream.DisposeAsyncScope().ConfigureAwait(false);
|
||||
#endif
|
||||
await SaveToAsync(stream, new GZipWriterOptions(CompressionType.GZip), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
@@ -47,24 +48,23 @@ public partial class GZipArchive
|
||||
{
|
||||
throw new InvalidFormatException("Only one entry is allowed in a GZip Archive");
|
||||
}
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var writer = new GZipWriter(stream, options);
|
||||
#else
|
||||
await using var writer = new GZipWriter(stream, options);
|
||||
#endif
|
||||
await foreach (
|
||||
var entry in oldEntries.WithCancellation(cancellationToken).ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
if (!entry.IsDirectory)
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var entryStream = await entry
|
||||
#if SYNC_ONLY
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
#else
|
||||
await using var entryStream = await entry
|
||||
#endif
|
||||
var entryStream = await entry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await using var entryStreamScope = entryStream
|
||||
.DisposeAsyncScope()
|
||||
.ConfigureAwait(false);
|
||||
#endif
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
@@ -77,13 +77,16 @@ public partial class GZipArchive
|
||||
}
|
||||
foreach (var entry in newEntries.Where(x => !x.IsDirectory))
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var entryStream = await entry
|
||||
#if SYNC_ONLY
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
#else
|
||||
await using var entryStream = await entry
|
||||
#endif
|
||||
var entryStream = await entry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await using var entryStreamScope = entryStream
|
||||
.DisposeAsyncScope()
|
||||
.ConfigureAwait(false);
|
||||
#endif
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
|
||||
@@ -77,13 +77,9 @@ public static partial class IArchiveEntryExtensions
|
||||
var entryStream = await archiveEntry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var entryStreamScope = entryStream;
|
||||
#else
|
||||
await using var entryStreamScope = entryStream
|
||||
.DisposeAsyncScope()
|
||||
.ConfigureAwait(false);
|
||||
#endif
|
||||
#endif
|
||||
var checkedStream = options is null
|
||||
? entryStream
|
||||
@@ -168,10 +164,11 @@ public static partial class IArchiveEntryExtensions
|
||||
options,
|
||||
async (x, fm, ct) =>
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
#if SYNC_ONLY
|
||||
using var fs = File.Open(x, fm);
|
||||
#else
|
||||
await using var fs = File.Open(x, fm);
|
||||
var fs = File.Open(x, fm);
|
||||
await using var scope = fs.DisposeAsyncScope().ConfigureAwait(false);
|
||||
#endif
|
||||
await entry
|
||||
.WriteToAsync(fs, options.BufferSize, options, null, ct)
|
||||
|
||||
@@ -83,11 +83,8 @@ public static class IWritableAsyncArchiveExtensions
|
||||
)
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
#else
|
||||
await using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
#endif
|
||||
var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
await using var scope = stream.DisposeAsyncScope().ConfigureAwait(false);
|
||||
await writableArchive.SaveToAsync(stream, options, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,7 @@ public partial class TarArchive
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var writer = new TarWriter(stream, options);
|
||||
#else
|
||||
await using var writer = new TarWriter(stream, options);
|
||||
#endif
|
||||
await foreach (
|
||||
var entry in oldEntries.WithCancellation(cancellationToken).ConfigureAwait(false)
|
||||
)
|
||||
@@ -43,13 +39,12 @@ public partial class TarArchive
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var entryStream = await entry
|
||||
#else
|
||||
await using var entryStream = await entry
|
||||
#endif
|
||||
var entryStream = await entry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await using var entryStreamScope = entryStream
|
||||
.DisposeAsyncScope()
|
||||
.ConfigureAwait(false);
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
@@ -75,15 +70,12 @@ public partial class TarArchive
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var entryStream = await entry
|
||||
var entryStream = await entry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
#else
|
||||
await using var entryStream = await entry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
await using var entryStreamScope = entryStream
|
||||
.DisposeAsyncScope()
|
||||
.ConfigureAwait(false);
|
||||
#endif
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
|
||||
@@ -84,11 +84,7 @@ public partial class ZipArchive
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var writer = new ZipWriter(stream, options);
|
||||
#else
|
||||
await using var writer = new ZipWriter(stream, options);
|
||||
#endif
|
||||
await foreach (
|
||||
var entry in oldEntries.WithCancellation(cancellationToken).ConfigureAwait(false)
|
||||
)
|
||||
@@ -105,13 +101,12 @@ public partial class ZipArchive
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var entryStream = await entry
|
||||
#else
|
||||
await using var entryStream = await entry
|
||||
#endif
|
||||
var entryStream = await entry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await using var entryStreamScope = entryStream
|
||||
.DisposeAsyncScope()
|
||||
.ConfigureAwait(false);
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
@@ -135,13 +130,12 @@ public partial class ZipArchive
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var entryStream = await entry
|
||||
#else
|
||||
await using var entryStream = await entry
|
||||
#endif
|
||||
var entryStream = await entry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await using var entryStreamScope = entryStream
|
||||
.DisposeAsyncScope()
|
||||
.ConfigureAwait(false);
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
|
||||
@@ -122,11 +122,7 @@ internal partial class MarkHeader
|
||||
{
|
||||
if (!leaveStreamOpen)
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
stream.Dispose();
|
||||
#else
|
||||
await stream.DisposeAsync().ConfigureAwait(false);
|
||||
#endif
|
||||
await stream.DisposeAsyncCompat().ConfigureAwait(false);
|
||||
}
|
||||
throw new InvalidFormatException("Error trying to read rar signature.", e);
|
||||
}
|
||||
|
||||
@@ -122,11 +122,7 @@ internal sealed class SevenZipStreamsCompressor(Stream outputStream)
|
||||
uint inputCrc2;
|
||||
long inputSize2;
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var lzma2Stream = new Lzma2EncoderStream(
|
||||
#else
|
||||
await using var lzma2Stream = new Lzma2EncoderStream(
|
||||
#endif
|
||||
outCrcStream,
|
||||
encoderProperties.DictionarySize,
|
||||
encoderProperties.NumFastBytes
|
||||
@@ -156,11 +152,7 @@ internal sealed class SevenZipStreamsCompressor(Stream outputStream)
|
||||
uint inputCrc;
|
||||
long inputSize;
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var lzmaStream = LzmaStream.Create(encoderProperties, false, outCrcStream);
|
||||
#else
|
||||
await using var lzmaStream = LzmaStream.Create(encoderProperties, false, outCrcStream);
|
||||
#endif
|
||||
properties = lzmaStream.Properties;
|
||||
|
||||
(inputCrc, inputSize) = await CopyWithCrcAsync(
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar;
|
||||
|
||||
internal class TarReadOnlySubStream : Stream
|
||||
internal class TarReadOnlySubStream : AsyncDisposableStream
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
|
||||
@@ -34,7 +35,6 @@ internal class TarReadOnlySubStream : Stream
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#if STREAM_DISPOSEASYNC
|
||||
public override async System.Threading.Tasks.ValueTask DisposeAsync()
|
||||
{
|
||||
if (_isDisposed)
|
||||
@@ -49,7 +49,6 @@ internal class TarReadOnlySubStream : Stream
|
||||
GC.SuppressFinalize(this);
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
private long BytesLeftToRead { get; set; }
|
||||
|
||||
|
||||
@@ -7,12 +7,7 @@ public abstract partial class Volume
|
||||
{
|
||||
public virtual async ValueTask DisposeAsync()
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
_actualStream.Dispose();
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
#else
|
||||
await _actualStream.DisposeAsync().ConfigureAwait(false);
|
||||
#endif
|
||||
await _actualStream.DisposeAsyncCompat().ConfigureAwait(false);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace SharpCompress.Common.Zip;
|
||||
|
||||
internal partial class WinzipAesCryptoStream
|
||||
{
|
||||
#if STREAM_DISPOSEASYNC
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_isDisposed)
|
||||
@@ -31,11 +30,10 @@ internal partial class WinzipAesCryptoStream
|
||||
_authentication.Dispose();
|
||||
_transform.Dispose();
|
||||
_cipher.Dispose();
|
||||
await _stream.DisposeAsync().ConfigureAwait(false);
|
||||
await _stream.DisposeAsyncCompat().ConfigureAwait(false);
|
||||
}
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
private async ValueTask VerifyAuthenticationAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -3,10 +3,11 @@ using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Security.Cryptography;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
internal partial class WinzipAesCryptoStream : Stream
|
||||
internal partial class WinzipAesCryptoStream : AsyncDisposableStream
|
||||
{
|
||||
private const int BLOCK_SIZE_IN_BYTES = 16;
|
||||
private readonly Aes _cipher;
|
||||
|
||||
@@ -23,6 +23,9 @@ public partial class ZlibStream
|
||||
|
||||
#if STREAM_DISPOSEASYNC
|
||||
public override async ValueTask DisposeAsync()
|
||||
#else
|
||||
public virtual async ValueTask DisposeAsync()
|
||||
#endif
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
@@ -33,9 +36,13 @@ public partial class ZlibStream
|
||||
{
|
||||
await _baseStream.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
#if STREAM_DISPOSEASYNC
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
#else
|
||||
base.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the stream.
|
||||
|
||||
@@ -33,6 +33,9 @@ using SharpCompress.Common;
|
||||
namespace SharpCompress.Compressors.Deflate;
|
||||
|
||||
public partial class ZlibStream : Stream
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
, IAsyncDisposable
|
||||
#endif
|
||||
{
|
||||
private readonly ZlibBaseStream _baseStream;
|
||||
private bool _disposed;
|
||||
|
||||
@@ -160,11 +160,7 @@ public class PpmdStream : Stream, IAsyncDisposable
|
||||
}
|
||||
catch
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
instance.Dispose();
|
||||
#else
|
||||
await instance.DisposeAsync().ConfigureAwait(false);
|
||||
#endif
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,14 +45,12 @@ internal sealed partial class ProgressReportingStream
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STREAM_DISPOSEASYNC
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (!_leaveOpen)
|
||||
{
|
||||
await _baseStream.DisposeAsync().ConfigureAwait(false);
|
||||
await _baseStream.DisposeAsyncCompat().ConfigureAwait(false);
|
||||
}
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace SharpCompress.IO;
|
||||
/// A stream wrapper that reports progress as data is read from the source.
|
||||
/// Used to track compression or extraction progress by wrapping the source stream.
|
||||
/// </summary>
|
||||
internal sealed partial class ProgressReportingStream : Stream
|
||||
internal sealed partial class ProgressReportingStream : AsyncDisposableStream
|
||||
{
|
||||
private readonly Stream _baseStream;
|
||||
private readonly IProgress<ProgressReport> _progress;
|
||||
|
||||
@@ -30,12 +30,12 @@ internal sealed partial class SeekableSharpCompressStream
|
||||
) => _stream.WriteAsync(buffer, cancellationToken);
|
||||
#endif
|
||||
|
||||
#if STREAM_DISPOSEASYNC
|
||||
public override ValueTask DisposeAsync()
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return base.DisposeAsync();
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (ThrowOnDispose)
|
||||
{
|
||||
@@ -46,11 +46,10 @@ internal sealed partial class SeekableSharpCompressStream
|
||||
_isDisposed = true;
|
||||
if (!LeaveStreamOpen)
|
||||
{
|
||||
_stream.Dispose();
|
||||
await _stream.DisposeAsyncCompat().ConfigureAwait(false);
|
||||
}
|
||||
return base.DisposeAsync();
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override Task WriteAsync(
|
||||
|
||||
@@ -253,6 +253,9 @@ public partial class SharpCompressStream
|
||||
|
||||
#if STREAM_DISPOSEASYNC
|
||||
public override async ValueTask DisposeAsync()
|
||||
#else
|
||||
public virtual async ValueTask DisposeAsync()
|
||||
#endif
|
||||
{
|
||||
if (!isDisposed)
|
||||
{
|
||||
@@ -267,7 +270,7 @@ public partial class SharpCompressStream
|
||||
{
|
||||
if (!LeaveStreamOpen)
|
||||
{
|
||||
await stream.DisposeAsync().ConfigureAwait(false);
|
||||
await stream.DisposeAsyncCompat().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -276,7 +279,11 @@ public partial class SharpCompressStream
|
||||
_ringBuffer = null;
|
||||
}
|
||||
}
|
||||
#if STREAM_DISPOSEASYNC
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
#else
|
||||
base.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace SharpCompress.IO;
|
||||
/// stream and usage scenario.
|
||||
/// </remarks>
|
||||
public partial class SharpCompressStream : Stream, IStreamStack
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
, IAsyncDisposable
|
||||
#endif
|
||||
{
|
||||
public virtual Stream BaseStream() => stream;
|
||||
|
||||
|
||||
@@ -104,11 +104,7 @@ public abstract partial class AbstractReader<TEntry, TVolume>
|
||||
}
|
||||
}
|
||||
//don't know the size so we have to try to decompress to skip
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
await using var s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
#endif
|
||||
await s.SkipEntryAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -138,11 +134,7 @@ public abstract partial class AbstractReader<TEntry, TVolume>
|
||||
|
||||
private async ValueTask WriteAsync(Stream writeStream, CancellationToken cancellationToken)
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
await using var s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
#endif
|
||||
var sourceStream = WrapWithProgress(s, Entry);
|
||||
await sourceStream
|
||||
.CopyToAsync(writeStream, Options.BufferSize, cancellationToken)
|
||||
|
||||
@@ -45,11 +45,8 @@ public static class IAsyncReaderExtensions
|
||||
options,
|
||||
async (x, fm, ct) =>
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var fs = File.Open(x, fm);
|
||||
#else
|
||||
await using var fs = File.Open(x, fm);
|
||||
#endif
|
||||
var fs = File.Open(x, fm);
|
||||
await using var scope = fs.DisposeAsyncScope().ConfigureAwait(false);
|
||||
await CopyEntryToAsync(reader, fs, options, ct).ConfigureAwait(false);
|
||||
},
|
||||
cancellationToken
|
||||
@@ -87,11 +84,8 @@ public static class IAsyncReaderExtensions
|
||||
options,
|
||||
async (x, fm, ct) =>
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var fs = File.Open(x, fm);
|
||||
#else
|
||||
await using var fs = File.Open(x, fm);
|
||||
#endif
|
||||
var fs = File.Open(x, fm);
|
||||
await using var scope = fs.DisposeAsyncScope().ConfigureAwait(false);
|
||||
await CopyEntryToAsync(reader, fs, options, ct).ConfigureAwait(false);
|
||||
},
|
||||
cancellationToken
|
||||
@@ -116,11 +110,7 @@ public static class IAsyncReaderExtensions
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var entryStream = await reader
|
||||
#else
|
||||
await using var entryStream = await reader
|
||||
#endif
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var checkedStream = IEntryExtensions.WrapWithChecksumValidation(
|
||||
|
||||
@@ -70,11 +70,8 @@ internal static partial class Utility
|
||||
)
|
||||
{
|
||||
// Use ReadOnlySubStream to limit reading and leverage framework's CopyToAsync
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var limitedStream = new IO.ReadOnlySubStream(source, maxLength);
|
||||
#else
|
||||
await using var limitedStream = new IO.ReadOnlySubStream(source, maxLength);
|
||||
#endif
|
||||
var limitedStream = new IO.ReadOnlySubStream(source, maxLength);
|
||||
await using var scope = limitedStream.DisposeAsyncScope().ConfigureAwait(false);
|
||||
await limitedStream
|
||||
.CopyToAsync(destination, bufferSize ?? Constants.BufferSize, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -77,11 +77,8 @@ public static class IWriterExtensions
|
||||
{
|
||||
throw new ArgumentException("Source does not exist: " + source.FullName);
|
||||
}
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var stream = source.OpenRead();
|
||||
#else
|
||||
await using var stream = source.OpenRead();
|
||||
#endif
|
||||
var stream = source.OpenRead();
|
||||
await using var scope = stream.DisposeAsyncScope().ConfigureAwait(false);
|
||||
await writer
|
||||
.WriteAsync(entryPath, stream, source.LastWriteTime, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -83,11 +83,7 @@ public partial class ZipWriter
|
||||
#if !SYNC_ONLY
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
#endif
|
||||
#if !STREAM_DISPOSEASYNC
|
||||
using var output = await WriteToStreamAsync(
|
||||
#else
|
||||
await using var output = await WriteToStreamAsync(
|
||||
#endif
|
||||
entryPath,
|
||||
zipWriterEntryOptions,
|
||||
cancellationToken
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Test.Mocks;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Streams;
|
||||
|
||||
public class AsyncDisposalCompatibilityTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async ValueTask SharpCompressStream_Forwards_Async_Disposal(bool seekable)
|
||||
{
|
||||
var source = new AsyncDisposeOnlyStream(seekable);
|
||||
var wrapper = SharpCompressStream.Create(source, 128);
|
||||
await ((IAsyncDisposable)wrapper).DisposeAsync().ConfigureAwait(false);
|
||||
Assert.True(source.AsyncDisposed);
|
||||
await ((IAsyncDisposable)wrapper).DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async ValueTask Stream_Disposal_Scope_Uses_Runtime_Async_Disposable()
|
||||
{
|
||||
var source = new AsyncDisposeOnlyStream(true);
|
||||
Stream stream = source;
|
||||
await using (stream.DisposeAsyncScope().ConfigureAwait(false)) { }
|
||||
Assert.True(source.AsyncDisposed);
|
||||
}
|
||||
|
||||
private sealed class AsyncDisposeOnlyStream(bool seekable) : AsyncOnlyStream(new MemoryStream())
|
||||
{
|
||||
public override bool CanSeek => seekable;
|
||||
public bool AsyncDisposed { get; private set; }
|
||||
|
||||
public override ValueTask DisposeAsync()
|
||||
{
|
||||
AsyncDisposed = true;
|
||||
return base.DisposeAsync();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && !AsyncDisposed)
|
||||
{
|
||||
throw new NotSupportedException("Synchronous disposal is not supported.");
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,6 @@ public class WinzipAesCryptoStreamTests
|
||||
{
|
||||
foreach (var completion in new[] { "dispose", "retry", "dispose-async" })
|
||||
{
|
||||
#if LEGACY_DOTNET
|
||||
if (completion == "dispose-async")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
yield return [length, tagBytesRead, corruption, completion];
|
||||
}
|
||||
}
|
||||
@@ -82,12 +76,10 @@ public class WinzipAesCryptoStreamTests
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
#if STREAM_DISPOSEASYNC
|
||||
else if (completion == "dispose-async")
|
||||
{
|
||||
await stream.DisposeAsync();
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
Assert.Equal(0, await stream.ReadAsync(new byte[1], 0, 1, CancellationToken.None));
|
||||
@@ -153,7 +145,6 @@ public class WinzipAesCryptoStreamTests
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
#if STREAM_DISPOSEASYNC
|
||||
[Fact]
|
||||
public async Task DisposeAsync_Verifies_Unread_Ciphertext()
|
||||
{
|
||||
@@ -171,7 +162,6 @@ public class WinzipAesCryptoStreamTests
|
||||
);
|
||||
await stream.DisposeAsync();
|
||||
}
|
||||
#endif
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, 0)]
|
||||
|
||||
@@ -286,7 +286,6 @@ public class TarArchiveAsyncTests : ArchiveTests
|
||||
Assert.Equal(0, opened.Length);
|
||||
}
|
||||
|
||||
#if STREAM_DISPOSEASYNC
|
||||
[Fact]
|
||||
public async ValueTask Tar_Save_New_Entry_Async_Only_Streams()
|
||||
{
|
||||
@@ -308,11 +307,11 @@ public class TarArchiveAsyncTests : ArchiveTests
|
||||
destination.Position = 0;
|
||||
await using var reopened = await TarArchive.OpenAsyncArchive(destination);
|
||||
var entry = await reopened.EntriesAsync.SingleAsync();
|
||||
await using var stream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
||||
var stream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
||||
await using var scope = stream.DisposeAsyncScope().ConfigureAwait(false);
|
||||
using var reader = new StreamReader(stream);
|
||||
Assert.Equal("test", await reader.ReadToEndAsync());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public async ValueTask Tar_Random_Write_Add_Async()
|
||||
|
||||
@@ -265,7 +265,6 @@ public class ZipArchiveAsyncTests : ArchiveTests
|
||||
Assert.Equal(0, opened.Length);
|
||||
}
|
||||
|
||||
#if STREAM_DISPOSEASYNC
|
||||
[Fact]
|
||||
public async ValueTask Zip_Save_New_Entry_Async_Only_Streams()
|
||||
{
|
||||
@@ -284,11 +283,11 @@ public class ZipArchiveAsyncTests : ArchiveTests
|
||||
destination.Position = 0;
|
||||
await using var reopened = await ZipArchive.OpenAsyncArchive(destination);
|
||||
var entry = await reopened.EntriesAsync.SingleAsync();
|
||||
await using var stream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
||||
var stream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
||||
await using var scope = stream.DisposeAsyncScope().ConfigureAwait(false);
|
||||
using var reader = new StreamReader(stream);
|
||||
Assert.Equal("test", await reader.ReadToEndAsync());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public async ValueTask Zip_Deflate_Entry_Stream_Async()
|
||||
|
||||
Reference in New Issue
Block a user