Small fixes and format

This commit is contained in:
Adam Hathcock
2026-04-30 10:41:33 +01:00
parent 29744b36be
commit 2aec75a3bc
11 changed files with 263 additions and 84 deletions

View File

@@ -152,7 +152,11 @@ internal sealed class SevenZipStreamsCompressor(Stream outputStream)
uint inputCrc;
long inputSize;
{
#if LEGACY_DOTNET
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(

View File

@@ -8,8 +8,11 @@ namespace SharpCompress.Compressors.Deflate;
public partial class DeflateStream
{
#if !LEGACY_DOTNET
#if !LEGACY_DOTNET || NETSTANDARD2_1
public override async ValueTask DisposeAsync()
#else
public async ValueTask DisposeAsync()
#endif
{
if (!_disposed)
{
@@ -19,9 +22,12 @@ public partial class DeflateStream
}
_disposed = true;
}
#if !LEGACY_DOTNET || NETSTANDARD2_1
await base.DisposeAsync().ConfigureAwait(false);
}
#else
await Task.CompletedTask.ConfigureAwait(false);
#endif
}
public override async Task FlushAsync(CancellationToken cancellationToken)
{

View File

@@ -550,15 +550,20 @@ internal class ZlibBaseStream : Stream, IStreamStack
}
}
#if !LEGACY_DOTNET
#if !LEGACY_DOTNET || NETSTANDARD2_1
public override async ValueTask DisposeAsync()
#else
public async ValueTask DisposeAsync()
#endif
{
if (isDisposed)
{
return;
}
isDisposed = true;
#if !LEGACY_DOTNET || NETSTANDARD2_1
await base.DisposeAsync().ConfigureAwait(false);
#endif
if (_stream is null)
{
return;
@@ -574,13 +579,19 @@ internal class ZlibBaseStream : Stream, IStreamStack
{
if (!_leaveOpen)
{
await _stream.DisposeAsync().ConfigureAwait(false);
if (_stream is IAsyncDisposable asyncDisposableStream)
{
await asyncDisposableStream.DisposeAsync().ConfigureAwait(false);
}
else
{
_stream.Dispose();
}
}
_stream = null;
}
}
}
#endif
public override void Flush()
{

View File

@@ -473,21 +473,30 @@ public class TarFactory
stream = writerOptions.CompressionType switch
{
CompressionType.None => stream,
CompressionType.BZip2 => await providers.CreateCompressStreamAsync(
CompressionType.BZip2,
stream,
writerOptions.CompressionLevel,
cancellationToken).ConfigureAwait(false),
CompressionType.GZip => await providers.CreateCompressStreamAsync(
CompressionType.GZip,
stream,
writerOptions.CompressionLevel,
cancellationToken).ConfigureAwait(false),
CompressionType.LZip => await providers.CreateCompressStreamAsync(
CompressionType.LZip,
stream,
writerOptions.CompressionLevel,
cancellationToken).ConfigureAwait(false),
CompressionType.BZip2 => await providers
.CreateCompressStreamAsync(
CompressionType.BZip2,
stream,
writerOptions.CompressionLevel,
cancellationToken
)
.ConfigureAwait(false),
CompressionType.GZip => await providers
.CreateCompressStreamAsync(
CompressionType.GZip,
stream,
writerOptions.CompressionLevel,
cancellationToken
)
.ConfigureAwait(false),
CompressionType.LZip => await providers
.CreateCompressStreamAsync(
CompressionType.LZip,
stream,
writerOptions.CompressionLevel,
cancellationToken
)
.ConfigureAwait(false),
_ => throw new InvalidFormatException(
"Tar does not support compression: " + writerOptions.CompressionType
),

View File

@@ -99,8 +99,11 @@ public class TarWrapper(
LZipStream.IsLZipFile,
LZipStream.IsLZipFileAsync,
(stream) => LZipStream.Create(stream, CompressionMode.Decompress),
async (stream, _) =>await LZipStream.CreateAsync(stream, CompressionMode.Decompress).ConfigureAwait(false),
["tar.lz"]
async (stream, _) =>
await LZipStream
.CreateAsync(stream, CompressionMode.Decompress)
.ConfigureAwait(false),
["tar.lz"]
),
new(
CompressionType.Xz,

View File

@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.IO;
@@ -37,6 +39,9 @@ internal class CountingStream : Stream
public override void Flush() => _stream.Flush();
public override async Task FlushAsync(CancellationToken cancellationToken) =>
await _stream.FlushAsync(cancellationToken).ConfigureAwait(false);
public override int Read(byte[] buffer, int offset, int count) =>
_stream.Read(buffer, offset, count);
@@ -56,6 +61,28 @@ internal class CountingStream : Stream
_bytesWritten++;
}
public override async Task WriteAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken
)
{
await _stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
_bytesWritten += count;
}
#if !LEGACY_DOTNET
public override async ValueTask WriteAsync(
ReadOnlyMemory<byte> buffer,
CancellationToken cancellationToken = default
)
{
await _stream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
_bytesWritten += buffer.Length;
}
#endif
protected override void Dispose(bool disposing)
{
if (disposing)

View File

@@ -39,11 +39,11 @@ public sealed class LZipCompressionProvider : CompressionProviderBase
return LZipStream.Create(source, CompressionMode.Decompress);
}
public override async ValueTask<Stream> CreateDecompressStreamAsync(Stream source, CancellationToken cancellationToken = default) => await LZipStream
.CreateAsync(
source,
CompressionMode.Decompress,
cancellationToken: cancellationToken
)
.ConfigureAwait(false);
public override async ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CancellationToken cancellationToken = default
) =>
await LZipStream
.CreateAsync(source, CompressionMode.Decompress, cancellationToken: cancellationToken)
.ConfigureAwait(false);
}

View File

@@ -17,7 +17,6 @@ public partial class TarWriter : AbstractWriter
_finalizeArchiveOnClose = options.FinalizeArchiveOnClose;
_headerFormat = options.HeaderFormat;
InitializeStream(destination);
}

View File

@@ -87,7 +87,12 @@ public partial class ZipWriter
)
{
cancellationToken.ThrowIfCancellationRequested();
options.ValidateWithFallback(compressionType, compressionLevel);
var compression = ToZipCompressionMethod(options.CompressionType ?? compressionType);
entryPath = NormalizeFilename(entryPath);
options.ModificationDateTime ??= DateTime.Now;
options.EntryComment ??= string.Empty;
var entry = new ZipCentralDirectoryEntry(
compression,
entryPath,
@@ -109,13 +114,16 @@ public partial class ZipWriter
await WriteHeaderAsync(entryPath, options, entry, useZip64, cancellationToken)
.ConfigureAwait(false);
streamPosition += headersize;
return new ZipWritingStream(
this,
OutputStream.NotNull(),
entry,
compression,
options.CompressionLevel ?? compressionLevel
);
return await ZipWritingStream
.CreateAsync(
this,
OutputStream.NotNull(),
entry,
compression,
options.CompressionLevel ?? compressionLevel,
cancellationToken
)
.ConfigureAwait(false);
}
private async Task<int> WriteHeaderAsync(

View File

@@ -392,13 +392,14 @@ public partial class ZipWriter : AbstractWriter
private readonly CRC32 crc = new();
private readonly ZipCentralDirectoryEntry entry;
private readonly Stream originalStream;
private readonly Stream writeStream;
private Stream writeStream;
private readonly ZipWriter writer;
private readonly ZipCompressionMethod zipCompressionMethod;
private readonly int compressionLevel;
private ICompressionProviderHooks? compressionProviderHooks;
private CompressionContext? compressionContext;
private CountingStream? counting;
private MemoryStream? asyncCompressionBuffer;
private ulong decompressed;
// Flag to prevent throwing exceptions on Dispose
@@ -410,7 +411,8 @@ public partial class ZipWriter : AbstractWriter
Stream originalStream,
ZipCentralDirectoryEntry entry,
ZipCompressionMethod zipCompressionMethod,
int compressionLevel
int compressionLevel,
Stream? compressionStream = null
)
{
this.writer = writer;
@@ -419,7 +421,42 @@ public partial class ZipWriter : AbstractWriter
this.entry = entry;
this.zipCompressionMethod = zipCompressionMethod;
this.compressionLevel = compressionLevel;
writeStream = GetWriteStream(originalStream);
writeStream = GetWriteStream(compressionStream ?? originalStream);
}
internal static async Task<ZipWritingStream> CreateAsync(
ZipWriter writer,
Stream originalStream,
ZipCentralDirectoryEntry entry,
ZipCompressionMethod zipCompressionMethod,
int compressionLevel,
CancellationToken cancellationToken
)
{
var compressionStream = originalStream;
MemoryStream? asyncCompressionBuffer = null;
if (
zipCompressionMethod
is ZipCompressionMethod.BZip2
or ZipCompressionMethod.LZMA
or ZipCompressionMethod.PPMd
)
{
asyncCompressionBuffer = new MemoryStream();
compressionStream = asyncCompressionBuffer;
}
var stream = new ZipWritingStream(
writer,
originalStream,
entry,
zipCompressionMethod,
compressionLevel,
compressionStream
);
stream.asyncCompressionBuffer = asyncCompressionBuffer;
await Task.CompletedTask.ConfigureAwait(false);
return stream;
}
public override bool CanRead => false;
@@ -674,43 +711,98 @@ public partial class ZipWriter : AbstractWriter
public override void Write(byte[] buffer, int offset, int count)
{
// We check the limits first, because we can keep the archive consistent
// if we can prevent the writes from happening
if (entry.Zip64HeaderOffset == 0)
{
var countingCount = counting?.BytesWritten ?? 0;
// Pre-check, the counting.Count is not exact, as we do not know the size before having actually compressed it
if (
limitsExceeded
|| ((decompressed + (uint)count) > uint.MaxValue)
|| (countingCount + (uint)count) > uint.MaxValue
)
{
throw new NotSupportedException(
"Attempted to write a stream that is larger than 4GiB without setting the zip64 option"
);
}
}
CheckWriteLimits(count);
decompressed += (uint)count;
crc.SlurpBlock(buffer, offset, count);
writeStream.Write(buffer, offset, count);
if (entry.Zip64HeaderOffset == 0)
CheckPostWriteLimits();
}
public override async Task WriteAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken
)
{
cancellationToken.ThrowIfCancellationRequested();
CheckWriteLimits(count);
decompressed += (uint)count;
crc.SlurpBlock(buffer, offset, count);
await writeStream
.WriteAsync(buffer, offset, count, cancellationToken)
.ConfigureAwait(false);
CheckPostWriteLimits();
}
#if !LEGACY_DOTNET
public override async ValueTask WriteAsync(
ReadOnlyMemory<byte> buffer,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
CheckWriteLimits(buffer.Length);
decompressed += (uint)buffer.Length;
if (System.Runtime.InteropServices.MemoryMarshal.TryGetArray(buffer, out var segment))
{
var countingCount = counting?.BytesWritten ?? 0;
// Post-check, this is accurate
if ((decompressed > uint.MaxValue) || countingCount > uint.MaxValue)
{
// We have written the data, so the archive is now broken
// Throwing the exception here, allows us to avoid
// throwing an exception in Dispose() which is discouraged
// as it can mask other errors
limitsExceeded = true;
throw new NotSupportedException(
"Attempted to write a stream that is larger than 4GiB without setting the zip64 option"
);
}
crc.SlurpBlock(segment.Array!, segment.Offset, segment.Count);
}
else
{
var array = buffer.ToArray();
crc.SlurpBlock(array, 0, array.Length);
}
await writeStream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
CheckPostWriteLimits();
}
#endif
private void CheckWriteLimits(int count)
{
// We check the limits first, because we can keep the archive consistent
// if we can prevent the writes from happening. The compressed byte count
// is only an estimate until compression has actually happened.
if (entry.Zip64HeaderOffset != 0)
{
return;
}
var countingCount = counting?.BytesWritten ?? 0;
if (
limitsExceeded
|| ((decompressed + (uint)count) > uint.MaxValue)
|| (countingCount + (uint)count) > uint.MaxValue
)
{
throw new NotSupportedException(
"Attempted to write a stream that is larger than 4GiB without setting the zip64 option"
);
}
}
private void CheckPostWriteLimits()
{
if (entry.Zip64HeaderOffset != 0)
{
return;
}
var countingCount = counting?.BytesWritten ?? 0;
if ((decompressed > uint.MaxValue) || countingCount > uint.MaxValue)
{
// We have written the data, so the archive is now broken. Throwing
// here avoids throwing from Dispose(), which can mask other errors.
limitsExceeded = true;
throw new NotSupportedException(
"Attempted to write a stream that is larger than 4GiB without setting the zip64 option"
);
}
}
@@ -777,20 +869,26 @@ public partial class ZipWriter : AbstractWriter
isDisposed = true;
#if NET48 || NETSTANDARD2_0
writeStream.Dispose();
#else
await writeStream.DisposeAsync().ConfigureAwait(false);
#endif
if (writeStream is IAsyncDisposable asyncDisposableWriteStream)
{
await asyncDisposableWriteStream.DisposeAsync().ConfigureAwait(false);
}
else
{
writeStream.Dispose();
}
if (limitsExceeded)
{
// We have written invalid data into the archive, so destroy it
#if NET48 || NETSTANDARD2_0
originalStream.Dispose();
#else
await originalStream.DisposeAsync().ConfigureAwait(false);
#endif
if (originalStream is IAsyncDisposable asyncDisposableOriginalStream)
{
await asyncDisposableOriginalStream.DisposeAsync().ConfigureAwait(false);
}
else
{
originalStream.Dispose();
}
return;
}
@@ -805,6 +903,14 @@ public partial class ZipWriter : AbstractWriter
var compressedvalue = zip64 ? uint.MaxValue : (uint)countingCount;
var decompressedvalue = zip64 ? uint.MaxValue : (uint)entry.Decompressed;
if (asyncCompressionBuffer is not null)
{
asyncCompressionBuffer.Position = 0;
await asyncCompressionBuffer
.CopyToAsync(originalStream, 81920, CancellationToken.None)
.ConfigureAwait(false);
}
if (originalStream.CanSeek)
{
originalStream.Position = (long)(entry.HeaderOffset + 6);

View File

@@ -130,7 +130,9 @@ public class LeaveOpenBehaviorTests
public void LZipStream_Compress_LeaveOpen_False()
{
using var innerStream = new TestStream(new MemoryStream());
using (var lzip = LZipStream.Create(innerStream, CompressionMode.Compress, leaveOpen: false))
using (
var lzip = LZipStream.Create(innerStream, CompressionMode.Compress, leaveOpen: false)
)
{
lzip.Write(CreateTestData(), 0, CreateTestData().Length);
lzip.Finish();
@@ -177,7 +179,9 @@ public class LeaveOpenBehaviorTests
using var innerStream = new TestStream(memStream);
var decompressed = new byte[CreateTestData().Length];
using (var lzip = LZipStream.Create(innerStream, CompressionMode.Decompress, leaveOpen: false))
using (
var lzip = LZipStream.Create(innerStream, CompressionMode.Decompress, leaveOpen: false)
)
{
lzip.Read(decompressed, 0, decompressed.Length);
}
@@ -201,7 +205,9 @@ public class LeaveOpenBehaviorTests
using var innerStream = new TestStream(memStream);
var decompressed = new byte[CreateTestData().Length];
using (var lzip = LZipStream.Create(innerStream, CompressionMode.Decompress, leaveOpen: true))
using (
var lzip = LZipStream.Create(innerStream, CompressionMode.Decompress, leaveOpen: true)
)
{
lzip.Read(decompressed, 0, decompressed.Length);
}