Zip ZStandard Writing with tests. Level support.

This commit is contained in:
Nanook
2025-07-24 00:23:55 +01:00
parent 624c385e27
commit c5de3d8cc1
13 changed files with 585 additions and 28 deletions

View File

@@ -28,4 +28,5 @@ public enum CompressionType
Squashed,
Crushed,
Distilled,
ZStandard,
}

View File

@@ -13,7 +13,7 @@ internal enum ZipCompressionMethod
Deflate64 = 9,
BZip2 = 12,
LZMA = 14,
ZStd = 93,
ZStandard = 93,
Xz = 95,
PPMd = 98,
WinzipAes = 0x63, //http://www.winzip.com/aes_info.htm

View File

@@ -46,6 +46,7 @@ public class ZipEntry : Entry
ZipCompressionMethod.Reduce3 => CompressionType.Reduce3,
ZipCompressionMethod.Reduce4 => CompressionType.Reduce4,
ZipCompressionMethod.Explode => CompressionType.Explode,
ZipCompressionMethod.ZStandard => CompressionType.ZStandard,
_ => CompressionType.Unknown,
};

View File

@@ -152,7 +152,7 @@ internal abstract class ZipFilePart : FilePart
{
return new XZStream(stream);
}
case ZipCompressionMethod.ZStd:
case ZipCompressionMethod.ZStandard:
{
return new DecompressionStream(stream);
}

View File

@@ -22,7 +22,7 @@ public sealed class GZipWriter : AbstractWriter
new GZipStream(
destination,
CompressionMode.Compress,
options?.CompressionLevel ?? CompressionLevel.Default,
(CompressionLevel)(options?.CompressionLevel ?? (int)CompressionLevel.Default),
WriterOptions.ArchiveEncoding.GetEncoding()
)
);

View File

@@ -1,24 +1,18 @@
using SharpCompress.Common;
using SharpCompress.Compressors.Deflate;
using D = SharpCompress.Compressors.Deflate;
namespace SharpCompress.Writers.GZip;
public class GZipWriterOptions : WriterOptions
{
public GZipWriterOptions()
: base(CompressionType.GZip) { }
: base(CompressionType.GZip, (int)(D.CompressionLevel.Default)) { }
internal GZipWriterOptions(WriterOptions options)
: base(options.CompressionType)
: base(options.CompressionType, (int)(D.CompressionLevel.Default))
{
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
if (options is GZipWriterOptions writerOptions)
{
CompressionLevel = writerOptions.CompressionLevel;
}
CompressionLevel = options.CompressionLevel;
}
public CompressionLevel CompressionLevel { get; set; } = CompressionLevel.Default;
}

View File

@@ -1,13 +1,41 @@
using SharpCompress.Common;
using SharpCompress.Common;
using D = SharpCompress.Compressors.Deflate;
namespace SharpCompress.Writers;
public class WriterOptions : OptionsBase
{
public WriterOptions(CompressionType compressionType) => CompressionType = compressionType;
public WriterOptions(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
};
}
public WriterOptions(CompressionType compressionType, int compressionLevel)
{
CompressionType = compressionType;
CompressionLevel = compressionLevel;
}
public CompressionType CompressionType { get; set; }
/// <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; set; }
public static implicit operator WriterOptions(CompressionType compressionType) =>
new(compressionType);
}

View File

@@ -18,7 +18,7 @@ namespace SharpCompress.Writers.Zip;
public class ZipWriter : AbstractWriter
{
private readonly CompressionType compressionType;
private readonly CompressionLevel compressionLevel;
private readonly int compressionLevel;
private readonly List<ZipCentralDirectoryEntry> entries = new();
private readonly string zipComment;
private long streamPosition;
@@ -36,7 +36,7 @@ public class ZipWriter : AbstractWriter
}
compressionType = zipWriterOptions.CompressionType;
compressionLevel = zipWriterOptions.DeflateCompressionLevel;
compressionLevel = zipWriterOptions.CompressionLevel;
if (WriterOptions.LeaveStreamOpen)
{
@@ -69,6 +69,7 @@ public class ZipWriter : AbstractWriter
CompressionType.BZip2 => ZipCompressionMethod.BZip2,
CompressionType.LZMA => ZipCompressionMethod.LZMA,
CompressionType.PPMd => ZipCompressionMethod.PPMd,
CompressionType.ZStandard => ZipCompressionMethod.ZStandard,
_ => throw new InvalidFormatException("Invalid compression method: " + compressionType),
};
@@ -117,7 +118,7 @@ public class ZipWriter : AbstractWriter
OutputStream.NotNull(),
entry,
compression,
options.DeflateCompressionLevel ?? compressionLevel
options.CompressionLevel ?? compressionLevel
);
}
@@ -312,7 +313,7 @@ public class ZipWriter : AbstractWriter
private readonly Stream writeStream;
private readonly ZipWriter writer;
private readonly ZipCompressionMethod zipCompressionMethod;
private readonly CompressionLevel compressionLevel;
private readonly int compressionLevel;
private SharpCompressStream? counting;
private ulong decompressed;
@@ -325,7 +326,7 @@ public class ZipWriter : AbstractWriter
Stream originalStream,
ZipCentralDirectoryEntry entry,
ZipCompressionMethod zipCompressionMethod,
CompressionLevel compressionLevel
int compressionLevel
)
{
this.writer = writer;
@@ -363,7 +364,7 @@ public class ZipWriter : AbstractWriter
}
case ZipCompressionMethod.Deflate:
{
return new DeflateStream(counting, CompressionMode.Compress, compressionLevel);
return new DeflateStream(counting, CompressionMode.Compress, (CompressionLevel)compressionLevel);
}
case ZipCompressionMethod.BZip2:
{
@@ -389,6 +390,10 @@ public class ZipWriter : AbstractWriter
counting.Write(writer.PpmdProperties.Properties, 0, 2);
return new PpmdStream(writer.PpmdProperties, counting, true);
}
case ZipCompressionMethod.ZStandard:
{
return new ZstdSharp.CompressionStream(counting, (int)writer.WriterOptions.CompressionLevel);
}
default:
{
throw new NotSupportedException("CompressionMethod: " + zipCompressionMethod);

View File

@@ -9,9 +9,29 @@ public class ZipWriterEntryOptions
public CompressionType? CompressionType { get; set; }
/// <summary>
/// When CompressionType.Deflate is used, this property is referenced. Defaults to CompressionLevel.Default.
/// 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)
/// When null, uses the archive's default compression level for the specified compression type.
/// Note: BZip2 and LZMA do not support compression levels in this implementation.
/// </summary>
public CompressionLevel? DeflateCompressionLevel { get; set; }
public int? CompressionLevel { get; set; }
/// <summary>
/// When CompressionType.Deflate is used, this property is referenced.
/// Valid range: 0-9 (0=no compression, 6=default, 9=best compression).
/// When null, uses the archive's default compression level.
/// </summary>
/// <remarks>
/// This property is deprecated. Use <see cref="CompressionLevel"/> instead.
/// </remarks>
[Obsolete("Use CompressionLevel property instead. This property will be removed in a future version.")]
public CompressionLevel? DeflateCompressionLevel
{
get => CompressionLevel.HasValue ? (CompressionLevel)Math.Min(CompressionLevel.Value, 9) : null;
set => CompressionLevel = value.HasValue ? (int)value.Value : null;
}
public string? EntryComment { get; set; }

View File

@@ -1,31 +1,66 @@
using System;
using SharpCompress.Common;
using SharpCompress.Compressors.Deflate;
using D = SharpCompress.Compressors.Deflate;
namespace SharpCompress.Writers.Zip;
public class ZipWriterOptions : WriterOptions
{
public ZipWriterOptions(CompressionType compressionType)
: base(compressionType) { }
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;
DeflateCompressionLevel = writerOptions.DeflateCompressionLevel;
ArchiveComment = writerOptions.ArchiveComment;
}
}
/// <summary>
/// When CompressionType.Deflate is used, this property is referenced. Defaults to CompressionLevel.Default.
/// Sets the compression level for Deflate compression (0-9).
/// This is a convenience method that sets the CompressionLevel property for Deflate compression.
/// </summary>
public CompressionLevel DeflateCompressionLevel { get; set; } = CompressionLevel.Default;
/// <param name="level">Deflate compression level (0=no compression, 6=default, 9=best compression)</param>
public void SetDeflateCompressionLevel(CompressionLevel level)
{
CompressionLevel = (int)level;
}
/// <summary>
/// Sets the compression level for ZStandard compression (1-22).
/// This is a convenience method that sets the CompressionLevel property for ZStandard compression.
/// </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;
}
/// <summary>
/// Legacy property for Deflate compression levels.
/// Valid range: 0-9 (0=no compression, 6=default, 9=best compression).
/// </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 string? ArchiveComment { get; set; }