Compare commits

...

1 Commits

Author SHA1 Message Date
Adam Hathcock
cc6e410be8 some options 2026-02-06 15:16:45 +00:00
50 changed files with 860 additions and 249 deletions

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Writers;
namespace SharpCompress.Archives;
@@ -111,7 +112,7 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
public async ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IWriterOptions options,
CancellationToken cancellationToken = default
)
{

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.IO;
using SharpCompress.Writers;
@@ -174,7 +175,7 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
return entry;
}
public void SaveTo(Stream stream, WriterOptions options)
public void SaveTo(Stream stream, IWriterOptions options)
{
//reset streams of new entries
newEntries.Cast<IWritableArchiveEntry>().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
@@ -210,14 +211,14 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
protected abstract void SaveTo(
Stream stream,
WriterOptions options,
IWriterOptions options,
IEnumerable<TEntry> oldEntries,
IEnumerable<TEntry> newEntries
);
protected abstract ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IWriterOptions options,
IAsyncEnumerable<TEntry> oldEntries,
IEnumerable<TEntry> newEntries,
CancellationToken cancellationToken = default

View File

@@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.GZip;
using SharpCompress.Common.Options;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
@@ -30,7 +31,7 @@ public partial class GZipArchive
protected override async ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IWriterOptions options,
IAsyncEnumerable<GZipArchiveEntry> oldEntries,
IEnumerable<GZipArchiveEntry> newEntries,
CancellationToken cancellationToken = default
@@ -40,7 +41,10 @@ public partial class GZipArchive
{
throw new InvalidFormatException("Only one entry is allowed in a GZip Archive");
}
using var writer = new GZipWriter(stream, new GZipWriterOptions(options));
using var writer = new GZipWriter(
stream,
options as GZipWriterOptions ?? new GZipWriterOptions(options)
);
await foreach (
var entry in oldEntries.WithCancellation(cancellationToken).ConfigureAwait(false)
)

View File

@@ -6,6 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.GZip;
using SharpCompress.Common.Options;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
@@ -58,7 +59,7 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
protected override void SaveTo(
Stream stream,
WriterOptions options,
IWriterOptions options,
IEnumerable<GZipArchiveEntry> oldEntries,
IEnumerable<GZipArchiveEntry> newEntries
)
@@ -67,7 +68,10 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
{
throw new InvalidFormatException("Only one entry is allowed in a GZip Archive");
}
using var writer = new GZipWriter(stream, new GZipWriterOptions(options));
using var writer = new GZipWriter(
stream,
options as GZipWriterOptions ?? new GZipWriterOptions(options)
);
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
{
using var entryStream = entry.OpenEntryStream();

View File

@@ -2,6 +2,7 @@ using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Options;
using SharpCompress.Writers;
namespace SharpCompress.Archives;
@@ -30,7 +31,7 @@ public interface IWritableArchive : IArchive, IWritableArchiveCommon
/// <summary>
/// Saves the archive to the specified stream using the given writer options.
/// </summary>
void SaveTo(Stream stream, WriterOptions options);
void SaveTo(Stream stream, IWriterOptions options);
/// <summary>
/// Removes the specified entry from the archive.
@@ -45,7 +46,7 @@ public interface IWritableAsyncArchive : IAsyncArchive, IWritableArchiveCommon
/// </summary>
ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IWriterOptions options,
CancellationToken cancellationToken = default
);

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Writers;
namespace SharpCompress.Archives;
@@ -58,13 +59,16 @@ public static class IWritableArchiveExtensions
);
}
public void SaveTo(string filePath, WriterOptions? options = null) =>
writableArchive.SaveTo(new FileInfo(filePath), options ?? new(CompressionType.Deflate));
public void SaveTo(string filePath, IWriterOptions? options = null) =>
writableArchive.SaveTo(
new FileInfo(filePath),
options ?? new WriterOptions(CompressionType.Deflate)
);
public void SaveTo(FileInfo fileInfo, WriterOptions? options = null)
public void SaveTo(FileInfo fileInfo, IWriterOptions? options = null)
{
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
writableArchive.SaveTo(stream, options ?? new(CompressionType.Deflate));
writableArchive.SaveTo(stream, options ?? new WriterOptions(CompressionType.Deflate));
}
}
}

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Writers;
namespace SharpCompress.Archives;
@@ -62,24 +63,28 @@ public static class IWritableAsyncArchiveExtensions
public ValueTask SaveToAsync(
string filePath,
WriterOptions? options = null,
IWriterOptions? options = null,
CancellationToken cancellationToken = default
) =>
writableArchive.SaveToAsync(
new FileInfo(filePath),
options ?? new(CompressionType.Deflate),
options ?? new WriterOptions(CompressionType.Deflate),
cancellationToken
);
public async ValueTask SaveToAsync(
FileInfo fileInfo,
WriterOptions? options = null,
IWriterOptions? options = null,
CancellationToken cancellationToken = default
)
{
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
await writableArchive
.SaveToAsync(stream, options ?? new(CompressionType.Deflate), cancellationToken)
.SaveToAsync(
stream,
options ?? new WriterOptions(CompressionType.Deflate),
cancellationToken
)
.ConfigureAwait(false);
}
}

View File

@@ -24,8 +24,7 @@ internal class FileInfoRarArchiveVolume : RarVolume
private static ReaderOptions FixOptions(ReaderOptions options)
{
//make sure we're closing streams with fileinfo
options.LeaveStreamOpen = false;
return options;
return options with { LeaveStreamOpen = false };
}
internal ReadOnlyCollection<RarFilePart> FileParts { get; }

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Common.Tar;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.IO;
@@ -18,13 +19,16 @@ public partial class TarArchive
{
protected override async ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IWriterOptions options,
IAsyncEnumerable<TarArchiveEntry> oldEntries,
IEnumerable<TarArchiveEntry> newEntries,
CancellationToken cancellationToken = default
)
{
using var writer = new TarWriter(stream, new TarWriterOptions(options));
using var writer = new TarWriter(
stream,
options as TarWriterOptions ?? new TarWriterOptions(options)
);
await foreach (
var entry in oldEntries.WithCancellation(cancellationToken).ConfigureAwait(false)
)

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Common.Tar;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.IO;
@@ -115,12 +116,15 @@ public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVo
protected override void SaveTo(
Stream stream,
WriterOptions options,
IWriterOptions options,
IEnumerable<TarArchiveEntry> oldEntries,
IEnumerable<TarArchiveEntry> newEntries
)
{
using var writer = new TarWriter(stream, new TarWriterOptions(options));
using var writer = new TarWriter(
stream,
options as TarWriterOptions ?? new TarWriterOptions(options)
);
foreach (var entry in oldEntries.Concat(newEntries))
{
if (entry.IsDirectory)

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Common.Zip;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.IO;
@@ -71,13 +72,16 @@ public partial class ZipArchive
protected override async ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IWriterOptions options,
IAsyncEnumerable<ZipArchiveEntry> oldEntries,
IEnumerable<ZipArchiveEntry> newEntries,
CancellationToken cancellationToken = default
)
{
using var writer = new ZipWriter(stream, new ZipWriterOptions(options));
using var writer = new ZipWriter(
stream,
options as ZipWriterOptions ?? new ZipWriterOptions(options)
);
await foreach (
var entry in oldEntries.WithCancellation(cancellationToken).ConfigureAwait(false)
)

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Common.Zip;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Compressors.Deflate;
@@ -113,12 +114,15 @@ public partial class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVo
protected override void SaveTo(
Stream stream,
WriterOptions options,
IWriterOptions options,
IEnumerable<ZipArchiveEntry> oldEntries,
IEnumerable<ZipArchiveEntry> newEntries
)
{
using var writer = new ZipWriter(stream, new ZipWriterOptions(options));
using var writer = new ZipWriter(
stream,
options as ZipWriterOptions ?? new ZipWriterOptions(options)
);
foreach (var entry in oldEntries.Concat(newEntries))
{
if (entry.IsDirectory)

View File

@@ -90,13 +90,15 @@ internal static partial class ExtractionMethods
{
if (entry.LinkTarget != null)
{
if (options?.WriteSymbolicLink is null)
if (options?.SymbolicLinkHandler is not null)
{
throw new ExtractionException(
"Entry is a symbolic link but ExtractionOptions.WriteSymbolicLink delegate is null"
);
options.SymbolicLinkHandler(destinationFileName, entry.LinkTarget);
}
options.WriteSymbolicLink(destinationFileName, entry.LinkTarget);
else
{
ExtractionOptions.DefaultSymbolicLinkHandler(destinationFileName, entry.LinkTarget);
}
return;
}
else
{

View File

@@ -101,13 +101,15 @@ internal static partial class ExtractionMethods
{
if (entry.LinkTarget != null)
{
if (options?.WriteSymbolicLink is null)
if (options?.SymbolicLinkHandler is not null)
{
throw new ExtractionException(
"Entry is a symbolic link but ExtractionOptions.WriteSymbolicLink delegate is null"
);
options.SymbolicLinkHandler(destinationFileName, entry.LinkTarget);
}
options.WriteSymbolicLink(destinationFileName, entry.LinkTarget);
else
{
ExtractionOptions.DefaultSymbolicLinkHandler(destinationFileName, entry.LinkTarget);
}
return;
}
else
{

View File

@@ -2,39 +2,107 @@ using System;
namespace SharpCompress.Common;
public class ExtractionOptions
/// <summary>
/// Options for configuring extraction behavior when extracting archive entries.
/// </summary>
/// <remarks>
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
/// <code>
/// var options = new ExtractionOptions { Overwrite = false };
/// options = options with { PreserveFileTime = true };
/// </code>
/// </remarks>
public sealed record ExtractionOptions
{
/// <summary>
/// overwrite target if it exists
/// Overwrite target if it exists.
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
/// </summary>
public bool Overwrite { get; set; }
public bool Overwrite { get; init; } = true;
/// <summary>
/// extract with internal directory structure
/// Extract with internal directory structure.
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
/// </summary>
public bool ExtractFullPath { get; set; }
public bool ExtractFullPath { get; init; } = true;
/// <summary>
/// preserve file time
/// Preserve file time.
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
/// </summary>
public bool PreserveFileTime { get; set; }
public bool PreserveFileTime { get; init; } = true;
/// <summary>
/// preserve windows file attributes
/// Preserve windows file attributes.
/// </summary>
public bool PreserveAttributes { get; set; }
public bool PreserveAttributes { get; init; }
/// <summary>
/// Delegate for writing symbolic links to disk.
/// sourcePath is where the symlink is created.
/// targetPath is what the symlink refers to.
/// The first parameter is the source path (where the symlink is created).
/// The second parameter is the target path (what the symlink refers to).
/// </summary>
public delegate void SymbolicLinkWriterDelegate(string sourcePath, string targetPath);
/// <remarks>
/// <b>Breaking change:</b> Changed from field to init-only property in version 0.40.0.
/// The default handler logs a warning message.
/// </remarks>
public Action<string, string>? SymbolicLinkHandler { get; init; }
public SymbolicLinkWriterDelegate WriteSymbolicLink = (sourcePath, targetPath) =>
/// <summary>
/// Creates a new ExtractionOptions instance with default values.
/// </summary>
public ExtractionOptions() { }
/// <summary>
/// Creates a new ExtractionOptions instance with the specified overwrite behavior.
/// </summary>
/// <param name="overwrite">Whether to overwrite existing files.</param>
public ExtractionOptions(bool overwrite)
{
Overwrite = overwrite;
}
/// <summary>
/// Creates a new ExtractionOptions instance with the specified extraction path and overwrite behavior.
/// </summary>
/// <param name="extractFullPath">Whether to preserve directory structure.</param>
/// <param name="overwrite">Whether to overwrite existing files.</param>
public ExtractionOptions(bool extractFullPath, bool overwrite)
{
ExtractFullPath = extractFullPath;
Overwrite = overwrite;
}
/// <summary>
/// Creates a new ExtractionOptions instance with the specified extraction path, overwrite behavior, and file time preservation.
/// </summary>
/// <param name="extractFullPath">Whether to preserve directory structure.</param>
/// <param name="overwrite">Whether to overwrite existing files.</param>
/// <param name="preserveFileTime">Whether to preserve file modification times.</param>
public ExtractionOptions(bool extractFullPath, bool overwrite, bool preserveFileTime)
{
ExtractFullPath = extractFullPath;
Overwrite = overwrite;
PreserveFileTime = preserveFileTime;
}
/// <summary>
/// Gets an ExtractionOptions instance configured for safe extraction (no overwrite).
/// </summary>
public static ExtractionOptions SafeExtract => new(overwrite: false);
/// <summary>
/// Gets an ExtractionOptions instance configured for flat extraction (no directory structure).
/// </summary>
public static ExtractionOptions FlatExtract => new(extractFullPath: false, overwrite: true);
/// <summary>
/// Default symbolic link handler that logs a warning message.
/// </summary>
public static void DefaultSymbolicLinkHandler(string sourcePath, string targetPath)
{
Console.WriteLine(
$"Could not write symlink {sourcePath} -> {targetPath}, for more information please see https://github.com/dotnet/runtime/issues/24271"
);
};
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.GZip;
@@ -7,7 +8,7 @@ public partial class GZipEntry
{
internal static async IAsyncEnumerable<GZipEntry> GetEntriesAsync(
Stream stream,
OptionsBase options
ReaderOptions options
)
{
yield return new GZipEntry(await GZipFilePart.CreateAsync(stream, options.ArchiveEncoding));

View File

@@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.GZip;
@@ -38,7 +39,7 @@ public partial class GZipEntry : Entry
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
internal static IEnumerable<GZipEntry> GetEntries(Stream stream, OptionsBase options)
internal static IEnumerable<GZipEntry> GetEntries(Stream stream, ReaderOptions options)
{
yield return new GZipEntry(GZipFilePart.Create(stream, options.ArchiveEncoding));
}

View File

@@ -9,7 +9,7 @@ public class GZipVolume : Volume
: base(stream, options, index) { }
public GZipVolume(FileInfo fileInfo, ReaderOptions options)
: base(fileInfo.OpenRead(), options) => options.LeaveStreamOpen = false;
: base(fileInfo.OpenRead(), options with { LeaveStreamOpen = false }) { }
public override bool IsFirstVolume => true;

View File

@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// This file is required for init-only properties to work on older target frameworks (.NET Framework 4.8, .NET Standard 2.0)
// The IsExternalInit type is used by the compiler for records and init-only properties
#if NETFRAMEWORK || NETSTANDARD2_0
using System.ComponentModel;
namespace System.Runtime.CompilerServices;
/// <summary>
/// Reserved to be used by the compiler for tracking metadata.
/// This class should not be used by developers in source code.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
internal static class IsExternalInit { }
#endif

View File

@@ -0,0 +1,6 @@
namespace SharpCompress.Common.Options;
public interface IEncodingOptions
{
IArchiveEncoding ArchiveEncoding { get; init; }
}

View File

@@ -0,0 +1,8 @@
using System;
namespace SharpCompress.Common.Options;
public interface IProgressOptions
{
IProgress<ProgressReport>? Progress { get; init; }
}

View File

@@ -0,0 +1,11 @@
namespace SharpCompress.Common.Options;
public interface IReaderOptions : IStreamOptions, IEncodingOptions, IProgressOptions
{
bool LookForHeader { get; init; }
string? Password { get; init; }
bool DisableCheckIncomplete { get; init; }
int BufferSize { get; init; }
string? ExtensionHint { get; init; }
int? RewindableBufferSize { get; init; }
}

View File

@@ -0,0 +1,6 @@
namespace SharpCompress.Common.Options;
public interface IStreamOptions
{
bool LeaveStreamOpen { get; init; }
}

View File

@@ -0,0 +1,9 @@
using SharpCompress.Common;
namespace SharpCompress.Common.Options;
public interface IWriterOptions : IStreamOptions, IEncodingOptions, IProgressOptions
{
CompressionType CompressionType { get; init; }
int CompressionLevel { get; init; }
}

View File

@@ -1,11 +0,0 @@
namespace SharpCompress.Common;
public class OptionsBase
{
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; set; } = true;
public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
@@ -7,6 +8,7 @@ using SharpCompress.Archives;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
@@ -156,27 +158,33 @@ public class GZipFactory
#region IWriterFactory
/// <inheritdoc/>
public IWriter OpenWriter(Stream stream, WriterOptions writerOptions)
public IWriter OpenWriter(Stream stream, IWriterOptions writerOptions)
{
if (writerOptions.CompressionType != CompressionType.GZip)
{
throw new InvalidFormatException("GZip archives only support GZip compression type.");
}
return new GZipWriter(stream, new GZipWriterOptions(writerOptions));
GZipWriterOptions gzipOptions = writerOptions switch
{
GZipWriterOptions gwo => gwo,
WriterOptions wo => new GZipWriterOptions(wo),
_ => throw new ArgumentException(
$"Expected WriterOptions or GZipWriterOptions, got {writerOptions.GetType().Name}",
nameof(writerOptions)
),
};
return new GZipWriter(stream, gzipOptions);
}
/// <inheritdoc/>
public IAsyncWriter OpenAsyncWriter(
Stream stream,
WriterOptions writerOptions,
IWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
if (writerOptions.CompressionType != CompressionType.GZip)
{
throw new InvalidFormatException("GZip archives only support GZip compression type.");
}
return (IAsyncWriter)OpenWriter(stream, writerOptions);
}

View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Tar;
@@ -216,13 +217,24 @@ public class TarFactory
#region IWriterFactory
/// <inheritdoc/>
public IWriter OpenWriter(Stream stream, WriterOptions writerOptions) =>
new TarWriter(stream, new TarWriterOptions(writerOptions));
public IWriter OpenWriter(Stream stream, IWriterOptions writerOptions)
{
TarWriterOptions tarOptions = writerOptions switch
{
TarWriterOptions two => two,
WriterOptions wo => new TarWriterOptions(wo),
_ => throw new ArgumentException(
$"Expected WriterOptions or TarWriterOptions, got {writerOptions.GetType().Name}",
nameof(writerOptions)
),
};
return new TarWriter(stream, tarOptions);
}
/// <inheritdoc/>
public IAsyncWriter OpenAsyncWriter(
Stream stream,
WriterOptions writerOptions,
IWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
@@ -5,6 +6,7 @@ using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Zip;
@@ -185,13 +187,24 @@ public class ZipFactory
#region IWriterFactory
/// <inheritdoc/>
public IWriter OpenWriter(Stream stream, WriterOptions writerOptions) =>
new ZipWriter(stream, new ZipWriterOptions(writerOptions));
public IWriter OpenWriter(Stream stream, IWriterOptions writerOptions)
{
ZipWriterOptions zipOptions = writerOptions switch
{
ZipWriterOptions zwo => zwo,
WriterOptions wo => new ZipWriterOptions(wo),
_ => throw new ArgumentException(
$"Expected WriterOptions or ZipWriterOptions, got {writerOptions.GetType().Name}",
nameof(writerOptions)
),
};
return new ZipWriter(stream, zipOptions);
}
/// <inheritdoc/>
public IAsyncWriter OpenAsyncWriter(
Stream stream,
WriterOptions writerOptions,
IWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{

View File

@@ -1,9 +1,20 @@
using System;
using SharpCompress.Common;
using SharpCompress.Common.Options;
namespace SharpCompress.Readers;
public class ReaderOptions : OptionsBase
/// <summary>
/// Options for configuring reader behavior when opening archives.
/// </summary>
/// <remarks>
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
/// <code>
/// var options = new ReaderOptions { Password = "secret" };
/// options = options with { LeaveStreamOpen = false };
/// </code>
/// </remarks>
public sealed record ReaderOptions : IReaderOptions
{
/// <summary>
/// The default buffer size for stream operations.
@@ -15,27 +26,46 @@ public class ReaderOptions : OptionsBase
)]
public const int DefaultBufferSize = 0x10000;
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; init; } = true;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
/// <summary>
/// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file)
/// </summary>
public bool LookForHeader { get; set; }
public bool LookForHeader { get; init; }
public string? Password { get; set; }
/// <summary>
/// Password for encrypted archives.
/// </summary>
public string? Password { get; init; }
public bool DisableCheckIncomplete { get; set; }
/// <summary>
/// Disable checking for incomplete archives.
/// </summary>
public bool DisableCheckIncomplete { get; init; }
public int BufferSize { get; set; } = Constants.BufferSize;
/// <summary>
/// Buffer size for stream operations.
/// </summary>
public int BufferSize { get; init; } = Constants.BufferSize;
/// <summary>
/// Provide a hint for the extension of the archive being read, can speed up finding the correct decoder. Should be without the leading period in the form like: tar.gz or zip
/// </summary>
public string? ExtensionHint { get; set; }
public string? ExtensionHint { get; init; }
/// <summary>
/// An optional progress reporter for tracking extraction operations.
/// When set, progress updates will be reported as entries are extracted.
/// </summary>
public IProgress<ProgressReport>? Progress { get; set; }
public IProgress<ProgressReport>? Progress { get; init; }
/// <summary>
/// Size of the rewindable buffer for non-seekable streams.
@@ -78,5 +108,73 @@ public class ReaderOptions : OptionsBase
/// using var reader = ReaderFactory.OpenReader(networkStream, options);
/// </code>
/// </example>
public int? RewindableBufferSize { get; set; }
public int? RewindableBufferSize { get; init; }
/// <summary>
/// Creates a new ReaderOptions instance with default values.
/// </summary>
public ReaderOptions() { }
/// <summary>
/// Creates a new ReaderOptions instance with the specified password.
/// </summary>
/// <param name="password">The password for encrypted archives.</param>
public ReaderOptions(string? password) => Password = password;
/// <summary>
/// Creates a new ReaderOptions instance with the specified password and header search option.
/// </summary>
/// <param name="password">The password for encrypted archives.</param>
/// <param name="lookForHeader">Whether to search for the archive header.</param>
public ReaderOptions(string? password, bool lookForHeader)
{
Password = password;
LookForHeader = lookForHeader;
}
/// <summary>
/// Creates a new ReaderOptions instance with the specified encoding.
/// </summary>
/// <param name="encoding">The encoding for archive entry names.</param>
public ReaderOptions(IArchiveEncoding encoding) => ArchiveEncoding = encoding;
/// <summary>
/// Creates a new ReaderOptions instance with the specified password and encoding.
/// </summary>
/// <param name="password">The password for encrypted archives.</param>
/// <param name="encoding">The encoding for archive entry names.</param>
public ReaderOptions(string? password, IArchiveEncoding encoding)
{
Password = password;
ArchiveEncoding = encoding;
}
/// <summary>
/// Creates a new ReaderOptions instance with the specified stream open behavior.
/// </summary>
/// <param name="leaveStreamOpen">Whether to leave the stream open after reading.</param>
public ReaderOptions(bool leaveStreamOpen)
{
LeaveStreamOpen = leaveStreamOpen;
}
/// <summary>
/// Creates a new ReaderOptions instance with the specified stream open behavior and password.
/// </summary>
/// <param name="leaveStreamOpen">Whether to leave the stream open after reading.</param>
/// <param name="password">The password for encrypted archives.</param>
public ReaderOptions(bool leaveStreamOpen, string? password)
{
LeaveStreamOpen = leaveStreamOpen;
Password = password;
}
/// <summary>
/// Creates a new ReaderOptions instance with the specified buffer size.
/// </summary>
/// <param name="bufferSize">The buffer size for stream operations.</param>
public ReaderOptions(int bufferSize)
{
BufferSize = bufferSize;
}
}

View File

@@ -1,14 +1,15 @@
using System;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.IO;
namespace SharpCompress.Writers;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public abstract partial class AbstractWriter(ArchiveType type, WriterOptions writerOptions)
public abstract partial class AbstractWriter(ArchiveType type, IWriterOptions writerOptions)
: IWriter,
IAsyncWriter
{
@@ -23,7 +24,7 @@ public abstract partial class AbstractWriter(ArchiveType type, WriterOptions wri
public ArchiveType WriterType { get; } = type;
protected WriterOptions WriterOptions { get; } = writerOptions;
protected IWriterOptions WriterOptions { get; } = writerOptions;
/// <summary>
/// Wraps the source stream with a progress-reporting stream if progress reporting is enabled.

View File

@@ -1,18 +1,100 @@
using System;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using D = SharpCompress.Compressors.Deflate;
namespace SharpCompress.Writers.GZip;
public class GZipWriterOptions : WriterOptions
/// <summary>
/// Options for configuring GZip writer behavior.
/// </summary>
/// <remarks>
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
/// <code>
/// var options = new GZipWriterOptions { CompressionLevel = 9 };
/// options = options with { LeaveStreamOpen = false };
/// </code>
/// </remarks>
public sealed record GZipWriterOptions : IWriterOptions
{
public GZipWriterOptions()
: base(CompressionType.GZip, (int)(D.CompressionLevel.Default)) { }
/// <summary>
/// The compression type (always GZip for this writer).
/// </summary>
public CompressionType CompressionType { get; init; } = CompressionType.GZip;
internal GZipWriterOptions(WriterOptions options)
: base(options.CompressionType, (int)(D.CompressionLevel.Default))
/// <summary>
/// The compression level to be used (0-9 for Deflate).
/// </summary>
public int CompressionLevel { get; init; } = (int)D.CompressionLevel.Default;
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; init; } = true;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
/// <summary>
/// Creates a new GZipWriterOptions instance with default values.
/// </summary>
public GZipWriterOptions() { }
/// <summary>
/// Creates a new GZipWriterOptions instance with the specified compression level.
/// </summary>
/// <param name="compressionLevel">The compression level (0-9).</param>
public GZipWriterOptions(int compressionLevel)
{
CompressionLevel = compressionLevel;
}
/// <summary>
/// Creates a new GZipWriterOptions instance with the specified Deflate compression level.
/// </summary>
/// <param name="compressionLevel">The Deflate compression level.</param>
public GZipWriterOptions(D.CompressionLevel compressionLevel)
{
CompressionLevel = (int)compressionLevel;
}
/// <summary>
/// Creates a new GZipWriterOptions instance with the specified stream open behavior.
/// </summary>
/// <param name="leaveStreamOpen">Whether to leave the stream open after writing.</param>
public GZipWriterOptions(bool leaveStreamOpen)
{
LeaveStreamOpen = leaveStreamOpen;
}
/// <summary>
/// Creates a new GZipWriterOptions instance from an existing WriterOptions instance.
/// </summary>
/// <param name="options">The WriterOptions to copy values from.</param>
public GZipWriterOptions(WriterOptions options)
{
CompressionLevel = options.CompressionLevel;
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
Progress = options.Progress;
}
/// <summary>
/// Creates a new GZipWriterOptions instance from an existing IWriterOptions instance.
/// </summary>
/// <param name="options">The IWriterOptions to copy values from.</param>
public GZipWriterOptions(IWriterOptions options)
{
CompressionLevel = options.CompressionLevel;
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
Progress = options.Progress;
}
}

View File

@@ -1,16 +1,17 @@
using System.IO;
using System.Threading;
using SharpCompress.Common.Options;
using SharpCompress.Factories;
namespace SharpCompress.Writers;
public interface IWriterFactory : IFactory
{
IWriter OpenWriter(Stream stream, WriterOptions writerOptions);
IWriter OpenWriter(Stream stream, IWriterOptions writerOptions);
IAsyncWriter OpenAsyncWriter(
Stream stream,
WriterOptions writerOptions,
IWriterOptions writerOptions,
CancellationToken cancellationToken = default
);
}

View File

@@ -1,11 +1,12 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using SharpCompress.Common.Options;
namespace SharpCompress.Writers;
public interface IWriterOpenable<TWriterOptions>
where TWriterOptions : WriterOptions
where TWriterOptions : IWriterOptions
{
public static abstract IWriter OpenWriter(string filePath, TWriterOptions writerOptions);

View File

@@ -1,33 +1,120 @@
using System;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Common.Tar.Headers;
namespace SharpCompress.Writers.Tar;
public class TarWriterOptions : WriterOptions
/// <summary>
/// Options for configuring Tar writer behavior.
/// </summary>
/// <remarks>
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
/// <code>
/// var options = new TarWriterOptions(CompressionType.GZip, true);
/// options = options with { HeaderFormat = TarHeaderWriteFormat.V7 };
/// </code>
/// </remarks>
public sealed record TarWriterOptions : IWriterOptions
{
/// <summary>
/// The compression type to use for the archive.
/// </summary>
public CompressionType CompressionType { get; init; }
/// <summary>
/// The compression level to be used when the compression type supports variable levels.
/// </summary>
public int CompressionLevel { get; init; }
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; init; } = true;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
/// <summary>
/// Indicates if archive should be finalized (by 2 empty blocks) on close.
/// </summary>
public bool FinalizeArchiveOnClose { get; }
public bool FinalizeArchiveOnClose { get; init; } = true;
public TarHeaderWriteFormat HeaderFormat { get; }
/// <summary>
/// The format to use when writing tar headers.
/// </summary>
public TarHeaderWriteFormat HeaderFormat { get; init; } =
TarHeaderWriteFormat.GNU_TAR_LONG_LINK;
/// <summary>
/// Creates a new TarWriterOptions instance with the specified compression type and finalization option.
/// </summary>
/// <param name="compressionType">The compression type for the archive.</param>
/// <param name="finalizeArchiveOnClose">Whether to finalize the archive on close.</param>
public TarWriterOptions(CompressionType compressionType, bool finalizeArchiveOnClose)
{
CompressionType = compressionType;
FinalizeArchiveOnClose = finalizeArchiveOnClose;
CompressionLevel = compressionType switch
{
CompressionType.ZStandard => 3,
_ => 0,
};
}
/// <summary>
/// Creates a new TarWriterOptions instance with the specified compression type, finalization option, and header format.
/// </summary>
/// <param name="compressionType">The compression type for the archive.</param>
/// <param name="finalizeArchiveOnClose">Whether to finalize the archive on close.</param>
/// <param name="headerFormat">The tar header format.</param>
public TarWriterOptions(
CompressionType compressionType,
bool finalizeArchiveOnClose,
TarHeaderWriteFormat headerFormat = TarHeaderWriteFormat.GNU_TAR_LONG_LINK
TarHeaderWriteFormat headerFormat
)
: base(compressionType)
: this(compressionType, finalizeArchiveOnClose)
{
FinalizeArchiveOnClose = finalizeArchiveOnClose;
HeaderFormat = headerFormat;
}
internal TarWriterOptions(WriterOptions options)
: this(options.CompressionType, true)
/// <summary>
/// Creates a new TarWriterOptions instance from an existing WriterOptions instance.
/// </summary>
/// <param name="options">The WriterOptions to copy values from.</param>
public TarWriterOptions(WriterOptions options)
{
LeaveStreamOpen = options.LeaveStreamOpen;
CompressionType = options.CompressionType;
CompressionLevel = options.CompressionLevel;
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
Progress = options.Progress;
}
/// <summary>
/// Creates a new TarWriterOptions instance from an existing IWriterOptions instance.
/// </summary>
/// <param name="options">The IWriterOptions to copy values from.</param>
public TarWriterOptions(IWriterOptions options)
{
CompressionType = options.CompressionType;
CompressionLevel = options.CompressionLevel;
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
Progress = options.Progress;
}
/// <summary>
/// Implicit conversion from CompressionType to TarWriterOptions with finalize enabled.
/// </summary>
/// <param name="compressionType">The compression type.</param>
public static implicit operator TarWriterOptions(CompressionType compressionType) =>
new(compressionType, true);
}

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using SharpCompress.Common;
using SharpCompress.Common.Options;
namespace SharpCompress.Writers;
@@ -11,7 +12,7 @@ public static class WriterFactory
public static IWriter OpenWriter(
string filePath,
ArchiveType archiveType,
WriterOptions writerOptions
IWriterOptions writerOptions
)
{
filePath.NotNullOrEmpty(nameof(filePath));
@@ -21,7 +22,7 @@ public static class WriterFactory
public static IWriter OpenWriter(
FileInfo fileInfo,
ArchiveType archiveType,
WriterOptions writerOptions
IWriterOptions writerOptions
)
{
fileInfo.NotNull(nameof(fileInfo));
@@ -31,7 +32,7 @@ public static class WriterFactory
public static IAsyncWriter OpenAsyncWriter(
string filePath,
ArchiveType archiveType,
WriterOptions writerOptions,
IWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{
@@ -47,7 +48,7 @@ public static class WriterFactory
public static IAsyncWriter OpenAsyncWriter(
FileInfo fileInfo,
ArchiveType archiveType,
WriterOptions writerOptions,
IWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{
@@ -63,7 +64,7 @@ public static class WriterFactory
public static IWriter OpenWriter(
Stream stream,
ArchiveType archiveType,
WriterOptions writerOptions
IWriterOptions writerOptions
)
{
var factory = Factories
@@ -89,7 +90,7 @@ public static class WriterFactory
public static IAsyncWriter OpenAsyncWriter(
Stream stream,
ArchiveType archiveType,
WriterOptions writerOptions,
IWriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{

View File

@@ -1,11 +1,58 @@
using System;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using D = SharpCompress.Compressors.Deflate;
namespace SharpCompress.Writers;
public class WriterOptions : OptionsBase
/// <summary>
/// Options for configuring writer behavior when creating archives.
/// </summary>
/// <remarks>
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
/// <code>
/// var options = new WriterOptions(CompressionType.Zip);
/// options = options with { LeaveStreamOpen = false };
/// </code>
/// </remarks>
public sealed record WriterOptions : IWriterOptions
{
/// <summary>
/// The compression type to use for the archive.
/// </summary>
public CompressionType CompressionType { get; init; }
/// <summary>
/// The compression level to be used when the compression type supports variable levels.
/// Valid ranges depend on the compression algorithm:
/// - Deflate/GZip: 0-9 (0=no compression, 6=default, 9=best compression)
/// - ZStandard: 1-22 (1=fastest, 3=default, 22=best compression)
/// Note: BZip2 and LZMA do not support compression levels in this implementation.
/// Defaults are set automatically based on compression type in the constructor.
/// </summary>
public int CompressionLevel { get; init; }
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; init; } = true;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// When set, progress updates will be reported as entries are written.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
/// <summary>
/// Creates a new WriterOptions instance with the specified compression type.
/// Compression level is automatically set based on the compression type.
/// </summary>
/// <param name="compressionType">The compression type for the archive.</param>
public WriterOptions(CompressionType compressionType)
{
CompressionType = compressionType;
@@ -19,30 +66,48 @@ public class WriterOptions : OptionsBase
};
}
/// <summary>
/// Creates a new WriterOptions instance with the specified compression type and level.
/// </summary>
/// <param name="compressionType">The compression type for the archive.</param>
/// <param name="compressionLevel">The compression level (algorithm-specific).</param>
public WriterOptions(CompressionType compressionType, int compressionLevel)
{
CompressionType = compressionType;
CompressionLevel = compressionLevel;
}
public CompressionType CompressionType { get; set; }
/// <summary>
/// Creates a new WriterOptions instance with the specified compression type and stream open behavior.
/// </summary>
/// <param name="compressionType">The compression type for the archive.</param>
/// <param name="leaveStreamOpen">Whether to leave the stream open after writing.</param>
public WriterOptions(CompressionType compressionType, bool leaveStreamOpen)
: this(compressionType)
{
LeaveStreamOpen = leaveStreamOpen;
}
/// <summary>
/// The compression level to be used when the compression type supports variable levels.
/// Valid ranges depend on the compression algorithm:
/// - Deflate/GZip: 0-9 (0=no compression, 6=default, 9=best compression)
/// - ZStandard: 1-22 (1=fastest, 3=default, 22=best compression)
/// Note: BZip2 and LZMA do not support compression levels in this implementation.
/// Defaults are set automatically based on compression type in the constructor.
/// Creates a new WriterOptions instance with the specified compression type, level, and stream open behavior.
/// </summary>
public int CompressionLevel { get; set; }
/// <param name="compressionType">The compression type for the archive.</param>
/// <param name="compressionLevel">The compression level (algorithm-specific).</param>
/// <param name="leaveStreamOpen">Whether to leave the stream open after writing.</param>
public WriterOptions(
CompressionType compressionType,
int compressionLevel,
bool leaveStreamOpen
)
: this(compressionType, compressionLevel)
{
LeaveStreamOpen = leaveStreamOpen;
}
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// When set, progress updates will be reported as entries are written.
/// Implicit conversion from CompressionType to WriterOptions.
/// </summary>
public IProgress<ProgressReport>? Progress { get; set; }
/// <param name="compressionType">The compression type.</param>
public static implicit operator WriterOptions(CompressionType compressionType) =>
new(compressionType);
}

View File

@@ -1,78 +1,52 @@
using System;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Compressors.Deflate;
using D = SharpCompress.Compressors.Deflate;
namespace SharpCompress.Writers.Zip;
public class ZipWriterOptions : WriterOptions
/// <summary>
/// Options for configuring Zip writer behavior.
/// </summary>
/// <remarks>
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
/// <code>
/// var options = new ZipWriterOptions(CompressionType.Zip);
/// options = options with { UseZip64 = true };
/// </code>
/// </remarks>
public sealed record ZipWriterOptions : IWriterOptions
{
public ZipWriterOptions(
CompressionType compressionType,
CompressionLevel compressionLevel = D.CompressionLevel.Default
)
: base(compressionType, (int)compressionLevel) { }
internal ZipWriterOptions(WriterOptions options)
: base(options.CompressionType)
{
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
CompressionLevel = options.CompressionLevel;
if (options is ZipWriterOptions writerOptions)
{
UseZip64 = writerOptions.UseZip64;
ArchiveComment = writerOptions.ArchiveComment;
}
}
/// <summary>
/// The compression type to use for the archive.
/// </summary>
public CompressionType CompressionType { get; init; }
/// <summary>
/// Sets the compression level for Deflate compression (0-9).
/// This is a convenience method that sets the CompressionLevel property for Deflate compression.
/// The compression level to be used when the compression type supports variable levels.
/// </summary>
/// <param name="level">Deflate compression level (0=no compression, 6=default, 9=best compression)</param>
public void SetDeflateCompressionLevel(CompressionLevel level)
{
CompressionLevel = (int)level;
}
public int CompressionLevel { get; init; }
/// <summary>
/// Sets the compression level for ZStandard compression (1-22).
/// This is a convenience method that sets the CompressionLevel property for ZStandard compression.
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
/// <param name="level">ZStandard compression level (1=fastest, 3=default, 22=best compression)</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when level is not between 1 and 22</exception>
public void SetZStandardCompressionLevel(int level)
{
if (level < 1 || level > 22)
{
throw new ArgumentOutOfRangeException(
nameof(level),
"ZStandard compression level must be between 1 and 22"
);
}
CompressionLevel = level;
}
public bool LeaveStreamOpen { get; init; } = true;
/// <summary>
/// Legacy property for Deflate compression levels.
/// Valid range: 0-9 (0=no compression, 6=default, 9=best compression).
/// Encoding to use for archive entry names.
/// </summary>
/// <remarks>
/// This property is deprecated. Use <see cref="WriterOptions.CompressionLevel"/> or <see cref="SetDeflateCompressionLevel"/> instead.
/// </remarks>
[Obsolete(
"Use CompressionLevel property or SetDeflateCompressionLevel method instead. This property will be removed in a future version."
)]
public CompressionLevel DeflateCompressionLevel
{
get => (CompressionLevel)Math.Min(CompressionLevel, 9);
set => CompressionLevel = (int)value;
}
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
public string? ArchiveComment { get; set; }
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
/// <summary>
/// Optional comment for the archive.
/// </summary>
public string? ArchiveComment { get; init; }
/// <summary>
/// Sets a value indicating if zip64 support is enabled.
@@ -81,5 +55,72 @@ public class ZipWriterOptions : WriterOptions
/// Archives larger than 4GiB are supported as long as all streams
/// are less than 4GiB in length.
/// </summary>
public bool UseZip64 { get; set; }
public bool UseZip64 { get; init; }
/// <summary>
/// Creates a new ZipWriterOptions instance with the specified compression type.
/// </summary>
/// <param name="compressionType">The compression type for the archive.</param>
public ZipWriterOptions(CompressionType compressionType)
{
CompressionType = compressionType;
CompressionLevel = compressionType switch
{
CompressionType.ZStandard => 3,
CompressionType.Deflate => (int)D.CompressionLevel.Default,
CompressionType.Deflate64 => (int)D.CompressionLevel.Default,
CompressionType.GZip => (int)D.CompressionLevel.Default,
_ => 0,
};
}
/// <summary>
/// Creates a new ZipWriterOptions instance with the specified compression type and level.
/// </summary>
/// <param name="compressionType">The compression type for the archive.</param>
/// <param name="compressionLevel">The compression level (algorithm-specific).</param>
public ZipWriterOptions(CompressionType compressionType, int compressionLevel)
{
CompressionType = compressionType;
CompressionLevel = compressionLevel;
}
/// <summary>
/// Creates a new ZipWriterOptions instance with the specified compression type and Deflate compression level.
/// </summary>
/// <param name="compressionType">The compression type for the archive.</param>
/// <param name="compressionLevel">The Deflate compression level.</param>
public ZipWriterOptions(CompressionType compressionType, D.CompressionLevel compressionLevel)
: this(compressionType, (int)compressionLevel) { }
/// <summary>
/// Creates a new ZipWriterOptions instance from an existing WriterOptions instance.
/// </summary>
/// <param name="options">The WriterOptions to copy values from.</param>
public ZipWriterOptions(WriterOptions options)
: this(options.CompressionType, options.CompressionLevel)
{
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
Progress = options.Progress;
}
/// <summary>
/// Creates a new ZipWriterOptions instance from an existing IWriterOptions instance.
/// </summary>
/// <param name="options">The IWriterOptions to copy values from.</param>
public ZipWriterOptions(IWriterOptions options)
: this(options.CompressionType, options.CompressionLevel)
{
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
Progress = options.Progress;
}
/// <summary>
/// Implicit conversion from CompressionType to ZipWriterOptions.
/// </summary>
/// <param name="compressionType">The compression type.</param>
public static implicit operator ZipWriterOptions(CompressionType compressionType) =>
new(compressionType);
}

View File

@@ -401,11 +401,9 @@ public class ArchiveTests : ReaderTests
int? compressionLevel = null
)
{
var writerOptions = new ZipWriterOptions(compressionType);
if (compressionLevel.HasValue)
{
writerOptions.CompressionLevel = compressionLevel.Value;
}
var writerOptions = compressionLevel.HasValue
? new WriterOptions(compressionType, compressionLevel.Value)
: new WriterOptions(compressionType);
return WriterFactory.OpenWriter(stream, ArchiveType.Zip, writerOptions);
}
@@ -415,12 +413,9 @@ public class ArchiveTests : ReaderTests
int? compressionLevel = null
)
{
var writerOptions = new ZipWriterOptions(compressionType);
if (compressionLevel.HasValue)
{
writerOptions.CompressionLevel = compressionLevel.Value;
writerOptions.LeaveStreamOpen = true;
}
var writerOptions = compressionLevel.HasValue
? new WriterOptions(compressionType, compressionLevel.Value, leaveStreamOpen: true)
: new WriterOptions(compressionType) { LeaveStreamOpen = true };
return WriterFactory.OpenAsyncWriter(
new AsyncOnlyStream(stream),
ArchiveType.Zip,

View File

@@ -27,7 +27,11 @@ public class ExtractionTests : TestBase
using (var stream = File.Create(testArchive))
{
using var writer = (ZipWriter)
WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate);
WriterFactory.OpenWriter(
stream,
ArchiveType.Zip,
new WriterOptions(CompressionType.Deflate)
);
// Create a test file to add to the archive
var testFilePath = Path.Combine(SCRATCH2_FILES_PATH, "testfile.txt");
@@ -72,7 +76,11 @@ public class ExtractionTests : TestBase
using (var stream = File.Create(testArchive))
{
using var writer = (ZipWriter)
WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate);
WriterFactory.OpenWriter(
stream,
ArchiveType.Zip,
new WriterOptions(CompressionType.Deflate)
);
var testFilePath = Path.Combine(SCRATCH2_FILES_PATH, "testfile2.txt");
File.WriteAllText(testFilePath, "Test content");

View File

@@ -27,7 +27,7 @@ public class GZipWriterAsyncTests : WriterTests
var writer = WriterFactory.OpenAsyncWriter(
new AsyncOnlyStream(stream),
ArchiveType.GZip,
CompressionType.GZip
new WriterOptions(CompressionType.GZip)
)
)
{
@@ -67,7 +67,7 @@ public class GZipWriterAsyncTests : WriterTests
using var writer = WriterFactory.OpenWriter(
new AsyncOnlyStream(stream),
ArchiveType.GZip,
CompressionType.BZip2
new WriterOptions(CompressionType.BZip2)
);
});

View File

@@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Writers;
using SharpCompress.Writers.GZip;
@@ -22,7 +22,11 @@ public class GZipWriterTests : WriterTests
)
)
using (
var writer = WriterFactory.OpenWriter(stream, ArchiveType.GZip, CompressionType.GZip)
var writer = WriterFactory.OpenWriter(
stream,
ArchiveType.GZip,
new WriterOptions(CompressionType.GZip)
)
)
{
writer.Write("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));
@@ -61,7 +65,7 @@ public class GZipWriterTests : WriterTests
using var writer = WriterFactory.OpenWriter(
stream,
ArchiveType.GZip,
CompressionType.BZip2
new WriterOptions(CompressionType.BZip2)
);
});

View File

@@ -41,11 +41,11 @@ public abstract class ReaderTests : TestBase
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
options ??= new ReaderOptions { BufferSize = 0x20000 };
options.LeaveStreamOpen = true;
readImpl(testArchive, options);
var optionsWithStreamOpen = options with { LeaveStreamOpen = true };
readImpl(testArchive, optionsWithStreamOpen);
options.LeaveStreamOpen = false;
readImpl(testArchive, options);
var optionsWithStreamClosed = options with { LeaveStreamOpen = false };
readImpl(testArchive, optionsWithStreamClosed);
VerifyFiles();
}
@@ -141,11 +141,21 @@ public abstract class ReaderTests : TestBase
options ??= new ReaderOptions() { BufferSize = 0x20000 };
options.LeaveStreamOpen = true;
await ReadImplAsync(testArchive, expectedCompression, options, cancellationToken);
var optionsWithStreamOpen = options with { LeaveStreamOpen = true };
await ReadImplAsync(
testArchive,
expectedCompression,
optionsWithStreamOpen,
cancellationToken
);
options.LeaveStreamOpen = false;
await ReadImplAsync(testArchive, expectedCompression, options, cancellationToken);
var optionsWithStreamClosed = options with { LeaveStreamOpen = false };
await ReadImplAsync(
testArchive,
expectedCompression,
optionsWithStreamClosed,
cancellationToken
);
VerifyFiles();
}

View File

@@ -148,8 +148,10 @@ public class TarArchiveAsyncTests : ArchiveTests
await using (var archive = TarArchive.CreateAsyncArchive())
{
await archive.AddAllFromDirectoryAsync(ORIGINAL_FILES_PATH);
var twopt = new TarWriterOptions(CompressionType.None, true);
twopt.ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) };
var twopt = new TarWriterOptions(CompressionType.None, true)
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) },
};
await archive.SaveToAsync(scratchPath, twopt);
}
CompareArchivesByPath(unmodified, scratchPath);
@@ -196,8 +198,7 @@ public class TarArchiveAsyncTests : ArchiveTests
{
using var mstm = new MemoryStream();
var enc = new ArchiveEncoding { Default = Encoding.UTF8 };
var twopt = new TarWriterOptions(CompressionType.None, true);
twopt.ArchiveEncoding = enc;
var twopt = new TarWriterOptions(CompressionType.None, true) { ArchiveEncoding = enc };
var fname = new string((char)0x3042, length);
using (var tw = new TarWriter(mstm, twopt))
using (var input = new MemoryStream(new byte[32]))

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Writers;
using Xunit;
namespace SharpCompress.Test.Tar;
@@ -80,7 +81,7 @@ public class TarArchiveDirectoryTests : TestBase
using (var fileStream = File.Create(scratchPath))
{
archive.SaveTo(fileStream, CompressionType.None);
archive.SaveTo(fileStream, new WriterOptions(CompressionType.None));
}
}

View File

@@ -34,7 +34,13 @@ public class TarArchiveTests : ArchiveTests
// Step 1: create a tar file containing a file with the test name
using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive)))
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.None))
using (
var writer = WriterFactory.OpenWriter(
stream,
ArchiveType.Tar,
new WriterOptions(CompressionType.None)
)
)
using (Stream inputStream = new MemoryStream())
{
var sw = new StreamWriter(inputStream);
@@ -94,7 +100,13 @@ public class TarArchiveTests : ArchiveTests
// Step 1: create a tar file containing a file with a long name
using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive)))
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.None))
using (
var writer = WriterFactory.OpenWriter(
stream,
ArchiveType.Tar,
new WriterOptions(CompressionType.None)
)
)
using (Stream inputStream = new MemoryStream())
{
var sw = new StreamWriter(inputStream);
@@ -162,8 +174,10 @@ public class TarArchiveTests : ArchiveTests
using (var archive = TarArchive.CreateArchive())
{
archive.AddAllFromDirectory(ORIGINAL_FILES_PATH);
var twopt = new TarWriterOptions(CompressionType.None, true);
twopt.ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) };
var twopt = new TarWriterOptions(CompressionType.None, true)
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) },
};
archive.SaveTo(scratchPath, twopt);
}
CompareArchivesByPath(unmodified, scratchPath);
@@ -180,7 +194,7 @@ public class TarArchiveTests : ArchiveTests
using (var archive = TarArchive.OpenArchive(unmodified))
{
archive.AddEntry("jpg\\test.jpg", jpg);
archive.SaveTo(scratchPath, CompressionType.None);
archive.SaveTo(scratchPath, new WriterOptions(CompressionType.None));
}
CompareArchivesByPath(modified, scratchPath);
}
@@ -198,7 +212,7 @@ public class TarArchiveTests : ArchiveTests
x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
);
archive.RemoveEntry(entry);
archive.SaveTo(scratchPath, CompressionType.None);
archive.SaveTo(scratchPath, new WriterOptions(CompressionType.None));
}
CompareArchivesByPath(modified, scratchPath);
}
@@ -228,8 +242,7 @@ public class TarArchiveTests : ArchiveTests
{
using var mstm = new MemoryStream();
var enc = new ArchiveEncoding { Default = Encoding.UTF8 };
var twopt = new TarWriterOptions(CompressionType.None, true);
twopt.ArchiveEncoding = enc;
var twopt = new TarWriterOptions(CompressionType.None, true) { ArchiveEncoding = enc };
var fname = new string((char)0x3042, length);
using (var tw = new TarWriter(mstm, twopt))
using (var input = new MemoryStream(new byte[32]))

View File

@@ -31,7 +31,7 @@ public class Zip64VersionConsistencyTests : WriterTests
}
// Create archive with UseZip64=true
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
var writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
LeaveStreamOpen = false,
UseZip64 = true,
@@ -135,7 +135,7 @@ public class Zip64VersionConsistencyTests : WriterTests
}
// Create archive without UseZip64
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
var writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
LeaveStreamOpen = false,
UseZip64 = false,
@@ -186,7 +186,7 @@ public class Zip64VersionConsistencyTests : WriterTests
File.Delete(filename);
}
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.LZMA)
var writerOptions = new ZipWriterOptions(CompressionType.LZMA)
{
LeaveStreamOpen = false,
UseZip64 = false,
@@ -239,7 +239,7 @@ public class Zip64VersionConsistencyTests : WriterTests
File.Delete(filename);
}
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.PPMd)
var writerOptions = new ZipWriterOptions(CompressionType.PPMd)
{
LeaveStreamOpen = false,
UseZip64 = false,
@@ -292,7 +292,7 @@ public class Zip64VersionConsistencyTests : WriterTests
File.Delete(filename);
}
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
var writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
LeaveStreamOpen = false,
UseZip64 = true,

View File

@@ -133,8 +133,10 @@ public class ZipArchiveAsyncTests : ArchiveTests
);
await archive.RemoveEntryAsync(entry);
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate);
writerOptions.ArchiveEncoding.Default = Encoding.GetEncoding(866);
var writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) },
};
await archive.SaveToAsync(scratchPath, writerOptions);
}
@@ -153,8 +155,10 @@ public class ZipArchiveAsyncTests : ArchiveTests
{
await archive.AddEntryAsync("jpg\\test.jpg", jpg);
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate);
writerOptions.ArchiveEncoding.Default = Encoding.GetEncoding(866);
var writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) },
};
await archive.SaveToAsync(scratchPath, writerOptions);
}
@@ -172,8 +176,10 @@ public class ZipArchiveAsyncTests : ArchiveTests
archive.DeflateCompressionLevel = CompressionLevel.BestSpeed;
archive.AddAllFromDirectory(ORIGINAL_FILES_PATH);
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate);
writerOptions.ArchiveEncoding.Default = Encoding.UTF8;
var writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.UTF8 },
};
await archive.SaveToAsync(scratchPath, writerOptions);
}

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Writers;
using Xunit;
namespace SharpCompress.Test.Zip;
@@ -80,7 +81,7 @@ public class ZipArchiveDirectoryTests : TestBase
using (var fileStream = File.Create(scratchPath))
{
archive.SaveTo(fileStream, CompressionType.Deflate);
archive.SaveTo(fileStream, new WriterOptions(CompressionType.Deflate));
}
}

View File

@@ -242,8 +242,10 @@ public class ZipArchiveTests : ArchiveTests
);
archive.RemoveEntry(entry);
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate);
writerOptions.ArchiveEncoding.Default = Encoding.GetEncoding(866);
var writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) },
};
archive.SaveTo(scratchPath, writerOptions);
}
@@ -262,8 +264,10 @@ public class ZipArchiveTests : ArchiveTests
{
archive.AddEntry("jpg\\test.jpg", jpg);
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate);
writerOptions.ArchiveEncoding.Default = Encoding.GetEncoding(866);
var writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) },
};
archive.SaveTo(scratchPath, writerOptions);
}
@@ -281,8 +285,8 @@ public class ZipArchiveTests : ArchiveTests
var str = "test.txt";
var source = new MemoryStream(Encoding.UTF8.GetBytes(str));
arc.AddEntry("test.txt", source, true, source.Length);
arc.SaveTo(scratchPath1, CompressionType.Deflate);
arc.SaveTo(scratchPath2, CompressionType.Deflate);
arc.SaveTo(scratchPath1, new WriterOptions(CompressionType.Deflate));
arc.SaveTo(scratchPath2, new WriterOptions(CompressionType.Deflate));
}
Assert.Equal(new FileInfo(scratchPath1).Length, new FileInfo(scratchPath2).Length);
@@ -330,8 +334,8 @@ public class ZipArchiveTests : ArchiveTests
{
arc.AddEntry("1.txt", stream, false, stream.Length);
arc.AddEntry("2.txt", stream, false, stream.Length);
arc.SaveTo(scratchPath1, CompressionType.Deflate);
arc.SaveTo(scratchPath2, CompressionType.Deflate);
arc.SaveTo(scratchPath1, new WriterOptions(CompressionType.Deflate));
arc.SaveTo(scratchPath2, new WriterOptions(CompressionType.Deflate));
}
}
@@ -374,8 +378,10 @@ public class ZipArchiveTests : ArchiveTests
{
archive.AddAllFromDirectory(SCRATCH_FILES_PATH);
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate);
writerOptions.ArchiveEncoding.Default = Encoding.GetEncoding(866);
var writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(866) },
};
archive.SaveTo(scratchPath, writerOptions);
}
@@ -393,7 +399,7 @@ public class ZipArchiveTests : ArchiveTests
var archiveStream = new MemoryStream();
archive.SaveTo(archiveStream, CompressionType.LZMA);
archive.SaveTo(archiveStream, new WriterOptions(CompressionType.LZMA));
archiveStream.Position = 0;
@@ -622,7 +628,7 @@ public class ZipArchiveTests : ArchiveTests
var zipWriter = WriterFactory.OpenWriter(
stream,
ArchiveType.Zip,
CompressionType.Deflate
new WriterOptions(CompressionType.Deflate)
)
)
{

View File

@@ -286,7 +286,7 @@ public class ZipReaderTests : ReaderTests
var zipWriter = WriterFactory.OpenWriter(
stream,
ArchiveType.Zip,
CompressionType.Deflate
new WriterOptions(CompressionType.Deflate)
)
)
{