Merge pull request #1189 from adamhathcock/copilot/add-lzwreader-support

Add LzwReader support for .Z compressed archives
This commit is contained in:
Adam Hathcock
2026-02-10 13:29:15 +00:00
committed by GitHub
43 changed files with 582 additions and 369 deletions

View File

@@ -69,7 +69,7 @@ public static partial class ArchiveFactory
options ??= ReaderOptions.ForOwnedFile;
var factory = await FindFactoryAsync<IMultiArchiveFactory>(fileInfo, cancellationToken);
return factory.OpenAsyncArchive(filesArray, options, cancellationToken);
return factory.OpenAsyncArchive(filesArray, options);
}
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(

View File

@@ -22,11 +22,9 @@ public partial class GZipArchive
{
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IWritableAsyncArchive<GZipWriterOptions>)
OpenArchive(new FileInfo(path), readerOptions ?? new ReaderOptions());
@@ -107,43 +105,23 @@ public partial class GZipArchive
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(stream, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(stream, readerOptions);
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfo, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfo, readerOptions);
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(streams, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(streams, readerOptions);
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfos, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfos, readerOptions);
public static IWritableArchive<GZipWriterOptions> CreateArchive() => new GZipArchive();

View File

@@ -20,20 +20,17 @@ public interface IArchiveOpenable<TSync, TASync>
public static abstract TASync OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract TASync OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract TASync OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
}

View File

@@ -50,10 +50,8 @@ public interface IMultiArchiveFactory : IFactory
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions">reading options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
IAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
}

View File

@@ -22,14 +22,12 @@ public interface IMultiArchiveOpenable<TSync, TASync>
public static abstract TASync OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract TASync OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
}
#endif

View File

@@ -22,11 +22,9 @@ public partial class RarArchive
{
public static IRarAsyncArchive OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IRarAsyncArchive)OpenArchive(new FileInfo(path), readerOptions);
}
@@ -102,41 +100,33 @@ public partial class RarArchive
public static IRarAsyncArchive OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IRarAsyncArchive)OpenArchive(stream, readerOptions);
}
public static IRarAsyncArchive OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IRarAsyncArchive)OpenArchive(fileInfo, readerOptions);
}
public static IRarAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IRarAsyncArchive)OpenArchive(streams, readerOptions);
}
public static IRarAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IRarAsyncArchive)OpenArchive(fileInfos, readerOptions);
}

View File

@@ -16,13 +16,8 @@ public partial class SevenZipArchive
IMultiArchiveOpenable<IArchive, IAsyncArchive>
#endif
{
public static IAsyncArchive OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncArchive OpenAsyncArchive(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty("path");
return (IAsyncArchive)OpenArchive(new FileInfo(path), readerOptions ?? new ReaderOptions());
}
@@ -91,43 +86,32 @@ public partial class SevenZipArchive
);
}
public static IAsyncArchive OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(stream, readerOptions);
}
public static IAsyncArchive OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfo, readerOptions);
}
public static IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(streams, readerOptions);
}
public static IAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
}

View File

@@ -97,54 +97,28 @@ public partial class TarArchive
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(stream, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(stream, readerOptions);
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<TarWriterOptions>)
OpenArchive(new FileInfo(path), readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(new FileInfo(path), readerOptions);
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfo, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfo, readerOptions);
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(streams, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(streams, readerOptions);
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfos, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfos, readerOptions);
public static bool IsTarFile(string filePath) => IsTarFile(new FileInfo(filePath));

View File

@@ -97,53 +97,28 @@ public partial class ZipArchive
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(path, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(path, readerOptions);
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(stream, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(stream, readerOptions);
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfo, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfo, readerOptions);
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(streams, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(streams, readerOptions);
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfos, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfos, readerOptions);
public static bool IsZipFile(string filePath, string? password = null) =>
IsZipFile(new FileInfo(filePath), password);

View File

@@ -10,4 +10,5 @@ public enum ArchiveType
Arc,
Arj,
Ace,
Lzw,
}

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using SharpCompress.Readers;
namespace SharpCompress.Common.Lzw;
public partial class LzwEntry
{
internal static async IAsyncEnumerable<LzwEntry> GetEntriesAsync(
Stream stream,
ReaderOptions options,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
yield return new LzwEntry(
await LzwFilePart.CreateAsync(stream, options.ArchiveEncoding, cancellationToken),
options
);
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Options;
using SharpCompress.Readers;
namespace SharpCompress.Common.Lzw;
public partial class LzwEntry : Entry
{
private readonly LzwFilePart? _filePart;
internal LzwEntry(LzwFilePart? filePart, IReaderOptions readerOptions)
: base(readerOptions)
{
_filePart = filePart;
}
public override CompressionType CompressionType => CompressionType.Lzw;
public override long Crc => 0;
public override string? Key => _filePart?.FilePartName;
public override string? LinkTarget => null;
public override long CompressedSize => 0;
public override long Size => 0;
public override DateTime? LastModifiedTime => null;
public override DateTime? CreatedTime => null;
public override DateTime? LastAccessedTime => null;
public override DateTime? ArchivedTime => null;
public override bool IsEncrypted => false;
public override bool IsDirectory => false;
public override bool IsSplitAfter => false;
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
internal static IEnumerable<LzwEntry> GetEntries(Stream stream, ReaderOptions options)
{
yield return new LzwEntry(LzwFilePart.Create(stream, options.ArchiveEncoding), options);
}
// Async methods moved to LzwEntry.Async.cs
}

View File

@@ -0,0 +1,23 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Lzw;
internal sealed partial class LzwFilePart
{
internal static async ValueTask<LzwFilePart> CreateAsync(
Stream stream,
IArchiveEncoding archiveEncoding,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
var part = new LzwFilePart(stream, archiveEncoding);
// For non-seekable streams, we can't track position, so use 0 since the stream will be
// read sequentially from its current position.
part.EntryStartPosition = stream.CanSeek ? stream.Position : 0;
return part;
}
}

View File

@@ -0,0 +1,60 @@
using System.IO;
using SharpCompress.Compressors.Lzw;
namespace SharpCompress.Common.Lzw;
internal sealed partial class LzwFilePart : FilePart
{
private readonly Stream _stream;
private readonly string? _name;
internal static LzwFilePart Create(Stream stream, IArchiveEncoding archiveEncoding)
{
var part = new LzwFilePart(stream, archiveEncoding);
// For non-seekable streams, we can't track position, so use 0 since the stream will be
// read sequentially from its current position.
part.EntryStartPosition = stream.CanSeek ? stream.Position : 0;
return part;
}
private LzwFilePart(Stream stream, IArchiveEncoding archiveEncoding)
: base(archiveEncoding)
{
_stream = stream;
_name = DeriveFileName(stream);
}
internal long EntryStartPosition { get; private set; }
internal override string? FilePartName => _name;
internal override Stream GetCompressedStream() =>
new LzwStream(_stream) { IsStreamOwner = false };
internal override Stream GetRawStream() => _stream;
private static string? DeriveFileName(Stream stream)
{
// Unwrap SharpCompressStream to get to the underlying FileStream
var unwrappedStream = stream;
if (stream is SharpCompress.IO.IStreamStack streamStack)
{
unwrappedStream = streamStack.BaseStream();
}
// Try to derive filename from FileStream
if (unwrappedStream is FileStream fileStream && !string.IsNullOrEmpty(fileStream.Name))
{
var fileName = Path.GetFileName(fileStream.Name);
// Strip .Z extension if present
if (fileName.EndsWith(".Z", System.StringComparison.OrdinalIgnoreCase))
{
return fileName.Substring(0, fileName.Length - 2);
}
return fileName;
}
// Default name for non-file streams
return "data";
}
}

View File

@@ -0,0 +1,17 @@
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.Lzw;
public class LzwVolume : Volume
{
public LzwVolume(Stream stream, ReaderOptions? options, int index)
: base(stream, options, index) { }
public LzwVolume(FileInfo fileInfo, ReaderOptions options)
: base(fileInfo.OpenRead(), options with { LeaveStreamOpen = false }) { }
public override bool IsFirstVolume => true;
public override bool IsMultiVolume => false;
}

View File

@@ -18,6 +18,7 @@ public abstract class Factory : IFactory
RegisterFactory(new RarFactory());
RegisterFactory(new TarFactory()); //put tar before most
RegisterFactory(new GZipFactory());
RegisterFactory(new LzwFactory());
RegisterFactory(new ArcFactory());
RegisterFactory(new ArjFactory());
RegisterFactory(new AceFactory());

View File

@@ -95,13 +95,8 @@ public class GZipFactory
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
#endregion

View File

@@ -0,0 +1,94 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Compressors.Lzw;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Lzw;
using SharpCompress.Readers.Tar;
namespace SharpCompress.Factories;
/// <summary>
/// Represents the foundation factory of LZW archive.
/// </summary>
public class LzwFactory : Factory, IReaderFactory
{
#region IFactory
/// <inheritdoc/>
public override string Name => "Lzw";
/// <inheritdoc/>
public override ArchiveType? KnownArchiveType => ArchiveType.Lzw;
/// <inheritdoc/>
public override IEnumerable<string> GetSupportedExtensions()
{
yield return "z";
}
/// <inheritdoc/>
public override bool IsArchive(Stream stream, string? password = null) =>
LzwStream.IsLzwStream(stream);
/// <inheritdoc/>
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
CancellationToken cancellationToken = default
) => LzwStream.IsLzwStreamAsync(stream, cancellationToken);
#endregion
#region IReaderFactory
/// <inheritdoc/>
internal override bool TryOpenReader(
SharpCompressStream sharpCompressStream,
ReaderOptions options,
out IReader? reader
)
{
reader = null;
if (LzwStream.IsLzwStream(sharpCompressStream))
{
sharpCompressStream.Rewind();
using (var testStream = new LzwStream(sharpCompressStream) { IsStreamOwner = false })
{
if (TarArchive.IsTarFile(testStream))
{
sharpCompressStream.StopRecording();
reader = new TarReader(sharpCompressStream, options, CompressionType.Lzw);
return true;
}
}
sharpCompressStream.StopRecording();
reader = OpenReader(sharpCompressStream, options);
return true;
}
sharpCompressStream.Rewind();
return false;
}
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
LzwReader.OpenReader(stream, options);
/// <inheritdoc/>
public ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new(LzwReader.OpenAsyncReader(stream, options));
}
#endregion
}

View File

@@ -90,11 +90,9 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
}

View File

@@ -50,7 +50,7 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
SevenZipArchive.OpenAsyncArchive(stream, readerOptions, CancellationToken.None);
SevenZipArchive.OpenAsyncArchive(stream, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
@@ -58,7 +58,7 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
SevenZipArchive.OpenAsyncArchive(fileInfo, readerOptions, CancellationToken.None);
SevenZipArchive.OpenAsyncArchive(fileInfo, readerOptions);
#endregion
@@ -74,7 +74,7 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => SevenZipArchive.OpenAsyncArchive(streams, readerOptions, CancellationToken.None);
) => SevenZipArchive.OpenAsyncArchive(streams, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(
@@ -85,9 +85,8 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
) => SevenZipArchive.OpenAsyncArchive(fileInfos, readerOptions, cancellationToken);
ReaderOptions? readerOptions = null
) => SevenZipArchive.OpenAsyncArchive(fileInfos, readerOptions);
#endregion

View File

@@ -145,13 +145,8 @@ public class TarFactory
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
#endregion

View File

@@ -155,13 +155,8 @@ public class ZipFactory
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
}
ReaderOptions? readerOptions = null
) => (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
#endregion

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Readers.Ace;
@@ -34,24 +33,14 @@ public partial class AceReader
return new MultiVolumeAceReader(streams, options ?? new ReaderOptions());
}
public static IAsyncReader OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
}
@@ -66,11 +55,9 @@ public partial class AceReader
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}

View File

@@ -1,40 +1,27 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Readers.Arc;
public partial class ArcReader : IReaderOpenable
{
public static IAsyncReader OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}

View File

@@ -1,40 +1,27 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Readers.Arj;
public partial class ArjReader : IReaderOpenable
{
public static IAsyncReader OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}

View File

@@ -1,5 +1,4 @@
using System.IO;
using System.Threading;
namespace SharpCompress.Readers.GZip;
@@ -8,34 +7,22 @@ public partial class GZipReader
: IReaderOpenable
#endif
{
public static IAsyncReader OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}

View File

@@ -17,20 +17,17 @@ public interface IReaderOpenable
public static abstract IAsyncReader OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
}
#endif

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Common.Lzw;
namespace SharpCompress.Readers.Lzw;
public partial class LzwReader
{
/// <summary>
/// Returns entries asynchronously for streams that only support async reads.
/// </summary>
protected override IAsyncEnumerable<LzwEntry> GetEntriesAsync(Stream stream) =>
LzwEntry.GetEntriesAsync(stream, Options);
}

View File

@@ -0,0 +1,46 @@
using System.IO;
namespace SharpCompress.Readers.Lzw;
public partial class LzwReader
#if NET8_0_OR_GREATER
: IReaderOpenable
#endif
{
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
)
{
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenReader(new FileInfo(filePath), readerOptions);
}
public static IReader OpenReader(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull(nameof(fileInfo));
return OpenReader(fileInfo.OpenRead(), readerOptions);
}
public static IReader OpenReader(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
return new LzwReader(stream, options ?? new ReaderOptions());
}
}

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Common.Lzw;
namespace SharpCompress.Readers.Lzw;
public partial class LzwReader : AbstractReader<LzwEntry, LzwVolume>
{
private LzwReader(Stream stream, ReaderOptions options)
: base(options, ArchiveType.Lzw) => Volume = new LzwVolume(stream, options, 0);
public override LzwVolume Volume { get; }
protected override IEnumerable<LzwEntry> GetEntries(Stream stream) =>
LzwEntry.GetEntries(stream, Options);
// GetEntriesAsync moved to LzwReader.Async.cs
}

View File

@@ -1,40 +1,27 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Readers.Rar;
public partial class RarReader : IReaderOpenable
{
public static IAsyncReader OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
}

View File

@@ -77,7 +77,7 @@ public static partial class ReaderFactory
}
throw new InvalidFormatException(
"Cannot determine compressed stream type. Supported Reader Formats: Ace, Arc, Arj, Zip, GZip, BZip2, Tar, Rar, LZip, XZ, ZStandard"
"Cannot determine compressed stream type. Supported Reader Formats: Ace, Arc, Arj, Zip, GZip, BZip2, Tar, Rar, LZip, Lzw, XZ, ZStandard"
);
}
}

View File

@@ -1,5 +1,4 @@
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Readers.Tar;
@@ -9,34 +8,22 @@ public partial class TarReader
: IReaderOpenable
#endif
{
public static IAsyncReader OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}

View File

@@ -1,40 +1,27 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Readers.Zip;
public partial class ZipReader : IReaderOpenable
{
public static IAsyncReader OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}

View File

@@ -1,6 +1,5 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Writers.GZip;
@@ -25,33 +24,18 @@ public partial class GZipWriter : IWriterOpenable<GZipWriterOptions>
return new GZipWriter(stream, writerOptions);
}
public static IAsyncWriter OpenAsyncWriter(
string path,
GZipWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
public static IAsyncWriter OpenAsyncWriter(string path, GZipWriterOptions writerOptions)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(path, writerOptions);
}
public static IAsyncWriter OpenAsyncWriter(
Stream stream,
GZipWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
public static IAsyncWriter OpenAsyncWriter(Stream stream, GZipWriterOptions writerOptions)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(stream, writerOptions);
}
public static IAsyncWriter OpenAsyncWriter(
FileInfo fileInfo,
GZipWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
public static IAsyncWriter OpenAsyncWriter(FileInfo fileInfo, GZipWriterOptions writerOptions)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(fileInfo, writerOptions);
}
}

View File

@@ -19,24 +19,20 @@ public interface IWriterOpenable<TWriterOptions>
/// <param name="stream">The stream to write to.</param>
/// <param name="archiveType">The archive type.</param>
/// <param name="writerOptions">Writer options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task that returns an IWriter.</returns>
public static abstract IAsyncWriter OpenAsyncWriter(
Stream stream,
TWriterOptions writerOptions,
CancellationToken cancellationToken = default
TWriterOptions writerOptions
);
public static abstract IAsyncWriter OpenAsyncWriter(
string filePath,
TWriterOptions writerOptions,
CancellationToken cancellationToken = default
TWriterOptions writerOptions
);
public static abstract IAsyncWriter OpenAsyncWriter(
FileInfo fileInfo,
TWriterOptions writerOptions,
CancellationToken cancellationToken = default
TWriterOptions writerOptions
);
}
#endif

View File

@@ -1,6 +1,5 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Writers.Tar;
@@ -25,33 +24,18 @@ public partial class TarWriter : IWriterOpenable<TarWriterOptions>
return new TarWriter(stream, writerOptions);
}
public static IAsyncWriter OpenAsyncWriter(
string path,
TarWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
public static IAsyncWriter OpenAsyncWriter(string path, TarWriterOptions writerOptions)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(path, writerOptions);
}
public static IAsyncWriter OpenAsyncWriter(
Stream stream,
TarWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
public static IAsyncWriter OpenAsyncWriter(Stream stream, TarWriterOptions writerOptions)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(stream, writerOptions);
}
public static IAsyncWriter OpenAsyncWriter(
FileInfo fileInfo,
TarWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
public static IAsyncWriter OpenAsyncWriter(FileInfo fileInfo, TarWriterOptions writerOptions)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(fileInfo, writerOptions);
}
}

View File

@@ -1,6 +1,5 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Writers.Zip;
@@ -25,33 +24,18 @@ public partial class ZipWriter : IWriterOpenable<ZipWriterOptions>
return new ZipWriter(stream, writerOptions);
}
public static IAsyncWriter OpenAsyncWriter(
string path,
ZipWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
public static IAsyncWriter OpenAsyncWriter(string path, ZipWriterOptions writerOptions)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(path, writerOptions);
}
public static IAsyncWriter OpenAsyncWriter(
Stream stream,
ZipWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
public static IAsyncWriter OpenAsyncWriter(Stream stream, ZipWriterOptions writerOptions)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(stream, writerOptions);
}
public static IAsyncWriter OpenAsyncWriter(
FileInfo fileInfo,
ZipWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
public static IAsyncWriter OpenAsyncWriter(FileInfo fileInfo, ZipWriterOptions writerOptions)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(fileInfo, writerOptions);
}
}

View File

@@ -0,0 +1,43 @@
using System.IO;
using SharpCompress.Common;
using SharpCompress.Readers;
using SharpCompress.Readers.Lzw;
using Xunit;
namespace SharpCompress.Test.Lzw;
public class LzwReaderAsyncTests : ReaderTests
{
public LzwReaderAsyncTests() => UseExtensionInsteadOfNameToVerify = true;
[Fact]
public async System.Threading.Tasks.Task Lzw_Reader_Async()
{
await ReadAsync("Tar.tar.Z", CompressionType.Lzw);
}
[Fact]
public async System.Threading.Tasks.Task Lzw_Reader_Plain_Z_File_Async()
{
// Test async reading of a plain .Z file (not tar-wrapped) using LzwReader directly
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "large_test.txt.Z"));
using var reader = LzwReader.OpenReader(stream);
Assert.Equal(ArchiveType.Lzw, reader.ArchiveType);
Assert.True(reader.MoveToNextEntry());
var entry = reader.Entry;
Assert.NotNull(entry);
Assert.Equal(CompressionType.Lzw, entry.CompressionType);
// When opened as FileStream, key should be derived from filename
Assert.Equal("large_test.txt", entry.Key);
// Decompress asynchronously
using var entryStream = reader.OpenEntryStream();
using var ms = new MemoryStream();
await entryStream.CopyToAsync(ms);
Assert.Equal(22300, ms.Length);
}
}

View File

@@ -0,0 +1,91 @@
using System.IO;
using SharpCompress.Common;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Lzw;
using Xunit;
namespace SharpCompress.Test.Lzw;
public class LzwReaderTests : ReaderTests
{
public LzwReaderTests() => UseExtensionInsteadOfNameToVerify = true;
[Fact]
public void Lzw_Reader_Generic() => Read("Tar.tar.Z", CompressionType.Lzw);
[Fact]
public void Lzw_Reader_Generic2()
{
//read only as Lzw item
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.Z"));
using var reader = LzwReader.OpenReader(SharpCompressStream.CreateNonDisposing(stream));
while (reader.MoveToNextEntry())
{
// LZW doesn't have CRC or Size in header like GZip, so we just check the entry exists
Assert.NotNull(reader.Entry);
}
}
[Fact]
public void Lzw_Reader_Factory_Detects_Tar_Wrapper()
{
// Note: Testing with Tar.tar.Z because:
// 1. LzwStream only supports decompression, not compression
// 2. This tests the important tar wrapper detection code path in LzwFactory.TryOpenReader
// 3. Verifies that tar.Z files correctly return TarReader with CompressionType.Lzw
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.Z"));
using var reader = ReaderFactory.OpenReader(
stream,
new ReaderOptions { LeaveStreamOpen = false }
);
// Should detect as Tar archive with Lzw compression
Assert.Equal(ArchiveType.Tar, reader.ArchiveType);
Assert.True(reader.MoveToNextEntry());
Assert.NotNull(reader.Entry);
Assert.Equal(CompressionType.Lzw, reader.Entry.CompressionType);
}
[Fact]
public void Lzw_Reader_Plain_Z_File()
{
// Test with a plain .Z file (not tar-wrapped) using LzwReader directly
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "large_test.txt.Z"));
using var reader = LzwReader.OpenReader(stream);
Assert.True(reader.MoveToNextEntry());
var entry = reader.Entry;
Assert.NotNull(entry);
Assert.Equal(CompressionType.Lzw, entry.CompressionType);
// Entry key should be "large_test.txt" (stripped .Z extension) when opened via FileStream
Assert.Equal("large_test.txt", entry.Key);
// Decompress and verify content
using var entryStream = reader.OpenEntryStream();
using var ms = new MemoryStream();
entryStream.CopyTo(ms);
var decompressed = System.Text.Encoding.UTF8.GetString(ms.ToArray());
Assert.Equal(22300, ms.Length);
Assert.Contains("This is a test file for LZW compression testing", decompressed);
}
[Fact]
public void Lzw_Reader_Factory_Detects_Plain_Z_File()
{
// Test that ReaderFactory correctly identifies a plain .Z file (not tar-wrapped)
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "large_test.txt.Z"));
using var reader = ReaderFactory.OpenReader(stream);
// Should detect as Lzw archive (not Tar)
Assert.Equal(ArchiveType.Lzw, reader.ArchiveType);
Assert.True(reader.MoveToNextEntry());
Assert.NotNull(reader.Entry);
Assert.Equal(CompressionType.Lzw, reader.Entry.CompressionType);
// When opened via ReaderFactory with a non-FileStream, key defaults to "data"
Assert.NotNull(reader.Entry.Key);
}
}

View File

@@ -178,8 +178,7 @@ public abstract class ReaderTests : TestBase
await using (
var reader = await ReaderFactory.OpenAsyncReader(
new AsyncOnlyStream(testStream),
options,
cancellationToken
options
)
)
{

View File

@@ -91,8 +91,7 @@ public class WriterTests : TestBase
await using var reader = await ReaderFactory.OpenAsyncReader(
new AsyncOnlyStream(SharpCompressStream.CreateNonDisposing(stream)),
readerOptions,
cancellationToken
readerOptions
);
await reader.WriteAllToDirectoryAsync(SCRATCH_FILES_PATH, cancellationToken);
}

Binary file not shown.