use extension where appropriate with more fixes

This commit is contained in:
Adam Hathcock
2026-01-21 16:57:25 +00:00
parent 7b7eba8cd9
commit 8df9232171
16 changed files with 120 additions and 16 deletions

View File

@@ -42,4 +42,9 @@ public class GZipEntry : Entry
{
yield return new GZipEntry(GZipFilePart.Create(stream, options.ArchiveEncoding));
}
internal static async IAsyncEnumerable<GZipEntry> GetEntriesAsync(Stream stream, OptionsBase options)
{
yield return new GZipEntry(await GZipFilePart.CreateAsync(stream, options.ArchiveEncoding));
}
}

View File

@@ -75,7 +75,12 @@ internal sealed class GZipFilePart : FilePart
internal override string? FilePartName => _name;
internal override Stream GetCompressedStream() =>
new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default);
new DeflateStream(
_stream,
CompressionMode.Decompress,
CompressionLevel.Default,
leaveOpen: true
);
internal override Stream GetRawStream() => _stream;

View File

@@ -2,7 +2,7 @@ using System;
namespace SharpCompress.Common;
public interface IVolume : IDisposable
public interface IVolume : IDisposable, IAsyncDisposable
{
int Index { get; }

View File

@@ -1,11 +1,12 @@
using System;
using System.IO;
using System.Threading.Tasks;
using SharpCompress.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common;
public abstract class Volume : IVolume
public abstract class Volume : IVolume, IAsyncDisposable
{
private readonly Stream _baseStream;
private readonly Stream _actualStream;
@@ -58,4 +59,14 @@ public abstract class Volume : IVolume
Dispose(true);
GC.SuppressFinalize(this);
}
public virtual async ValueTask DisposeAsync()
{
#if NETFRAMEWORK || NETSTANDARD2_0
await Task.Run(() => _actualStream.Dispose()).ConfigureAwait(false);
#else
await _actualStream.DisposeAsync().ConfigureAwait(false);
#endif
GC.SuppressFinalize(this);
}
}

View File

@@ -57,19 +57,31 @@ public class DeflateStream : Stream, IStreamStack
private readonly ZlibBaseStream _baseStream;
private bool _disposed;
private readonly bool _leaveOpen;
public DeflateStream(
Stream stream,
CompressionMode mode,
CompressionLevel level = CompressionLevel.Default,
Encoding? forceEncoding = null
)
: this(stream, mode, level, leaveOpen: false, forceEncoding) { }
public DeflateStream(
Stream stream,
CompressionMode mode,
CompressionLevel level,
bool leaveOpen,
Encoding? forceEncoding = null
)
{
_leaveOpen = leaveOpen;
_baseStream = new ZlibBaseStream(
stream,
mode,
level,
ZlibStreamFlavor.DEFLATE,
leaveOpen,
forceEncoding
);
@@ -265,7 +277,7 @@ public class DeflateStream : Stream, IStreamStack
#if DEBUG_STREAMS
this.DebugDispose(typeof(DeflateStream));
#endif
if (disposing)
if (disposing && !_leaveOpen)
{
_baseStream?.Dispose();
}
@@ -286,7 +298,10 @@ public class DeflateStream : Stream, IStreamStack
#if DEBUG_STREAMS
this.DebugDispose(typeof(DeflateStream));
#endif
await _baseStream.DisposeAsync().ConfigureAwait(false);
if (!_leaveOpen)
{
await _baseStream.DisposeAsync().ConfigureAwait(false);
}
_disposed = true;
}
await base.DisposeAsync().ConfigureAwait(false);

View File

@@ -89,6 +89,7 @@ internal class ZlibBaseStream : Stream, IStreamStack
protected internal int _gzipHeaderByteCount;
private readonly Encoding _encoding;
private readonly bool _leaveOpen;
internal int Crc32 => crc?.Crc32Result ?? 0;
@@ -98,9 +99,20 @@ internal class ZlibBaseStream : Stream, IStreamStack
CompressionLevel level,
ZlibStreamFlavor flavor,
Encoding encoding
)
: this(stream, compressionMode, level, flavor, leaveOpen: false, encoding) { }
public ZlibBaseStream(
Stream stream,
CompressionMode compressionMode,
CompressionLevel level,
ZlibStreamFlavor flavor,
bool leaveOpen,
Encoding encoding
)
{
_flushMode = FlushType.None;
_leaveOpen = leaveOpen;
//this._workingBuffer = new byte[WORKING_BUFFER_SIZE_DEFAULT];
_stream = stream;
@@ -546,7 +558,10 @@ internal class ZlibBaseStream : Stream, IStreamStack
finally
{
end();
_stream?.Dispose();
if (!_leaveOpen)
{
_stream?.Dispose();
}
_stream = null;
}
}
@@ -577,7 +592,10 @@ internal class ZlibBaseStream : Stream, IStreamStack
end();
if (_stream != null)
{
await _stream.DisposeAsync().ConfigureAwait(false);
if (!_leaveOpen)
{
await _stream.DisposeAsync().ConfigureAwait(false);
}
_stream = null;
}
}

View File

@@ -16,12 +16,12 @@ public abstract class Factory : IFactory
{
RegisterFactory(new ZipFactory());
RegisterFactory(new RarFactory());
RegisterFactory(new SevenZipFactory());
RegisterFactory(new TarFactory());//put tar before most
RegisterFactory(new GZipFactory());
RegisterFactory(new TarFactory());
RegisterFactory(new ArcFactory());
RegisterFactory(new ArjFactory());
RegisterFactory(new AceFactory());
RegisterFactory(new SevenZipFactory());
}
private static readonly HashSet<Factory> _factories = new();

View File

@@ -67,7 +67,16 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IAsyncReader
{
await _entriesForCurrentReadStreamAsync.DisposeAsync();
}
Volume?.Dispose();
// If Volume implements IAsyncDisposable, use async disposal
if (Volume is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
else
{
Volume?.Dispose();
}
}
#endregion

View File

@@ -21,6 +21,11 @@ public partial class GZipReader : AbstractReader<GZipEntry, GZipVolume>
/// <param name="options"></param>
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
return new GZipReader(stream, options ?? new ReaderOptions());
}
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
return new GZipReader(stream, options ?? new ReaderOptions());
@@ -30,4 +35,7 @@ public partial class GZipReader : AbstractReader<GZipEntry, GZipVolume>
protected override IEnumerable<GZipEntry> GetEntries(Stream stream) =>
GZipEntry.GetEntries(stream, Options);
protected override IAsyncEnumerable<GZipEntry> GetEntriesAsync(Stream stream) {
return GZipEntry.GetEntriesAsync(stream, Options);
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using AwesomeAssertions;
using SharpCompress.Archives;
using SharpCompress.Common;
using SharpCompress.Compressors.Xz;
@@ -90,6 +91,7 @@ public class ArchiveTests : ReaderTests
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
ArchiveStreamRead(
ArchiveFactory.FindFactory<IArchiveFactory>(testArchive),
Path.GetExtension(testArchive).Substring(1),
readerOptions,
testArchive
);
@@ -102,36 +104,43 @@ public class ArchiveTests : ReaderTests
)
{
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
ArchiveStreamRead(archiveFactory, readerOptions, testArchive);
ArchiveStreamRead(archiveFactory, Path.GetExtension(testArchive), readerOptions, testArchive);
}
protected void ArchiveStreamRead(
string extension,
ReaderOptions? readerOptions = null,
params string[] testArchives
) =>
ArchiveStreamRead(
ArchiveFactory.FindFactory<IArchiveFactory>(testArchives[0]),
extension,
readerOptions,
testArchives
);
protected void ArchiveStreamRead(
IArchiveFactory archiveFactory,
string extension,
ReaderOptions? readerOptions = null,
params string[] testArchives
) =>
ArchiveStreamRead(
archiveFactory,
readerOptions,
testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x))
testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)),
extension
);
protected void ArchiveStreamRead(
IArchiveFactory archiveFactory,
ReaderOptions? readerOptions,
IEnumerable<string> testArchives
IEnumerable<string> testArchives,
string extension
)
{
extension.Should().BeOneOf(archiveFactory.GetSupportedExtensions());
foreach (var path in testArchives)
{
using (
@@ -290,6 +299,11 @@ public class ArchiveTests : ReaderTests
{
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
archiveFactory ??= ArchiveFactory.FindFactory<IArchiveFactory>(testArchive);
string extension = Path.GetExtension(testArchive).Substring(1);
if (!int.TryParse(extension, out _) && "exe" != extension) //exclude parts
{
extension.Should().BeOneOf(archiveFactory.GetSupportedExtensions());
}
using (var archive = archiveFactory.OpenArchive(new FileInfo(testArchive), readerOptions))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Compressors;
@@ -74,7 +75,7 @@ public class AsyncTests : TestBase
public async ValueTask Archive_Entry_Async_Open_Stream()
{
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz");
await using var archive = await ArchiveFactory.OpenAsyncArchive(
await using var archive = GZipArchive.OpenAsyncArchive(
new AsyncOnlyStream(File.OpenRead(testArchive))
);

View File

@@ -24,7 +24,7 @@ public class GZipArchiveAsyncTests : ArchiveTests
await using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
#endif
await using (
var archive = await ArchiveFactory.OpenAsyncArchive(new AsyncOnlyStream(stream))
var archive = GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream))
)
{
var entry = await archive.EntriesAsync.FirstAsync();

View File

@@ -17,7 +17,7 @@ public class GZipArchiveTests : ArchiveTests
public void GZip_Archive_Generic()
{
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
using (var archive = ArchiveFactory.OpenArchive(stream))
using (var archive = GZipArchive.OpenArchive(stream))
{
var entry = archive.Entries.First();
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull()));

View File

@@ -10,6 +10,7 @@ using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
using SharpCompress.Test.Mocks;
using Xunit;
@@ -149,6 +150,7 @@ public abstract class ReaderTests : TestBase
options.LeaveStreamOpen = false;
await ReadImplAsync(testArchive, expectedCompression, options, cancellationToken);
VerifyFiles();
}

View File

@@ -85,6 +85,7 @@ public class SevenZipArchiveTests : ArchiveTests
public void SevenZipArchive_BZip2_Split() =>
Assert.Throws<InvalidOperationException>(() =>
ArchiveStreamRead(
".7z",
null,
"Original.7z.001",
"Original.7z.002",
@@ -151,6 +152,7 @@ public class SevenZipArchiveTests : ArchiveTests
public void SevenZipArchive_ZSTD_Split() =>
Assert.Throws<InvalidOperationException>(() =>
ArchiveStreamRead(
".7z",
null,
"7Zip.ZSTD.Split.7z.001",
"7Zip.ZSTD.Split.7z.002",

View File

@@ -63,6 +63,20 @@ public class TestBase : IAsyncDisposable
Directory.Delete(SCRATCH2_FILES_PATH, true);
}
public void CleanScratch()
{
if (Directory.Exists(SCRATCH_FILES_PATH))
{
Directory.Delete(SCRATCH_FILES_PATH, true);
}
Directory.CreateDirectory(SCRATCH_FILES_PATH);
if (Directory.Exists(SCRATCH2_FILES_PATH))
{
Directory.Delete(SCRATCH2_FILES_PATH, true);
}
Directory.CreateDirectory(SCRATCH2_FILES_PATH);
}
public void VerifyFiles()
{
if (UseExtensionInsteadOfNameToVerify)