Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
e402958
Add Zlib Encoder and Decoder classes
iremyux Jan 12, 2026
95ac930
Add tests
iremyux Jan 12, 2026
88acc57
Merge branch 'dotnet:main' into 62113-zlib-encoder-decoder
iremyux Jan 13, 2026
6f1ab80
Use CompressionLevel instead of int
iremyux Jan 15, 2026
8926759
Use CompressionLevel instead of int
iremyux Jan 15, 2026
bb2d89c
Introduce seperate encoders and decoders
iremyux Jan 19, 2026
a21a800
Update the tests accordingly
iremyux Jan 19, 2026
4b87dcd
Remove ZlibEncoder and decoder with lower L
iremyux Jan 19, 2026
aabc3a6
Add ZLibEncoder and Decoder with capital L
iremyux Jan 19, 2026
cfb3d7e
Change GetMaxCompressedLengt's inputLength to long
iremyux Jan 19, 2026
f031eed
"File is required to end with a single newline character"
iremyux Jan 19, 2026
1d18eec
Add Interop layer for zlib's compressBound and use it inside GetMaxCo…
iremyux Jan 20, 2026
0cf5b04
Merge branch 'main' into 62113-zlib-encoder-decoder
iremyux Jan 23, 2026
01fba5a
Fix gzip's header+trailer size
iremyux Jan 23, 2026
03fbcbc
Add CompressionNative_CompressBound to pal_zlib.h
iremyux Jan 23, 2026
665ae10
Remove empty line
iremyux Jan 23, 2026
ee0a437
Add CompressionNative_CompressBound to entrypoints.c
iremyux Jan 26, 2026
238aa58
Use EncoderDecoderTestBase for tests
iremyux Feb 4, 2026
aeebd78
Expose quality and windowLog for encoders & remove compressionLevel enum
iremyux Feb 9, 2026
ab9ee4a
Add WindowLog to ZLibCompressionOptions
iremyux Feb 9, 2026
cdb1cb4
Change the parameter name from inputSize to inputLength
iremyux Feb 12, 2026
46c5a8f
Add WindowLog to ZLibCompressionOptions
iremyux Feb 12, 2026
e35f082
VAlidate inputLength
iremyux Feb 13, 2026
fb4b1c8
Remove EnsureInitialized
iremyux Feb 13, 2026
f82ddc4
Fix tests
iremyux Feb 13, 2026
89ba215
Fix tests
iremyux Feb 13, 2026
6f05f53
Change the parameter name from inputSize to inputLength
iremyux Feb 13, 2026
c6384e4
Remove unnecessary parameters
iremyux Feb 16, 2026
f3dd1c4
Move test to TestBase
iremyux Feb 16, 2026
91b2890
Remove unused library
iremyux Feb 16, 2026
e52c80e
Remove function body
iremyux Feb 16, 2026
802d228
Add EnsureNotDisposed method to Gzip & Zlib too
iremyux Feb 16, 2026
8177c6a
Match zstandard's compress errorcode maping
iremyux Feb 17, 2026
46cf8b4
Streams can now use windowLog
iremyux Feb 18, 2026
5c226ac
Add early check for source to Compress method
iremyux Feb 18, 2026
0c5eb13
Add empty destination check
iremyux Feb 25, 2026
38202d3
Add space
iremyux Feb 25, 2026
2b5ad84
Move stream check up
iremyux Feb 25, 2026
5bf24f0
Move CompressionFormat enum outside of DeflateEncoder
iremyux Feb 25, 2026
e56c631
Define max/min window log inside options, instead of refering to Defl…
iremyux Feb 25, 2026
382b7c2
Remove random allocation of test data
iremyux Feb 25, 2026
ba72b26
Add CompressionFormat.cs to the project
iremyux Feb 25, 2026
82038d8
Define constant inside Options
iremyux Feb 25, 2026
ae7c2a9
Calculate WindowLog for Gzip and raw Deflate
iremyux Feb 25, 2026
a1658e7
Use constants for Quality as well
iremyux Feb 25, 2026
d294740
Move min/max/default values of Quality&WindowLog to ZlibNative.cs
iremyux Feb 25, 2026
913b797
Add comments
iremyux Feb 25, 2026
c4948b8
Accept default values of quality and windowlog in validation method
iremyux Feb 25, 2026
fdfad6b
-1 will be accepted as default value for quality and windowlog
iremyux Feb 25, 2026
3486f09
Fix spaces
iremyux Feb 25, 2026
c5d2412
Comments for -1 being accepted for quality and windowlog as default
iremyux Feb 25, 2026
15f0fd6
Merge branch 'dotnet:main' into 62113-zlib-encoder-decoder
iremyux Feb 26, 2026
d6d47bf
Add min/max/default values for WindowLog
iremyux Mar 4, 2026
b1fa69c
Merge branch '62113-zlib-encoder-decoder' of https://github.com/iremy…
iremyux Mar 4, 2026
0ce58db
Change comment - copilot suggestion
iremyux Mar 4, 2026
0ffe1ee
Add the Zlib header and trailer to GetMaxCompressedLength
iremyux Mar 4, 2026
c7fca14
Merge branch '62113-zlib-encoder-decoder' of https://github.com/iremy…
iremyux Mar 4, 2026
8a31cc5
Move EncoderDecoderTestBase to common in test.csproj
iremyux Mar 4, 2026
f2a26ab
Remove redundant field initializations
iremyux Mar 4, 2026
93f42ce
Add tests with WindowLog
iremyux Mar 4, 2026
2ab5044
Better comments for GetMaxCompressedLength methods
iremyux Mar 4, 2026
9cb4ff5
missing blank line
iremyux Mar 4, 2026
e5f6990
Remove spaces
iremyux Mar 4, 2026
f2ed2e5
Fix getmaxCompressedLength and add tests
iremyux Mar 11, 2026
b8cd755
Merge remote-tracking branch 'upstream/main' into 62113-zlib-encoder-…
iremyux Mar 11, 2026
dbf1903
Remove some tests
iremyux Mar 11, 2026
b963848
Add missing checks
iremyux Mar 11, 2026
254cf71
Fix flush returns
iremyux Mar 11, 2026
23aab9a
Missing nameof
iremyux Mar 11, 2026
6274e44
Fix windowLog=8 error
iremyux Mar 12, 2026
274ad66
Undo unnecessary changes
iremyux Mar 19, 2026
da347ae
Move RoundTrip_WithWindowLog to StreamUnitTestBase
iremyux Mar 19, 2026
2a0b947
Add 'assert' to CompressionNative_CompressBound
iremyux Mar 19, 2026
fd5206f
Use Random instead of Enumerable
iremyux Mar 19, 2026
3e2e8bf
Remove duplicate tests
iremyux Mar 21, 2026
4aaaace
Address review comments related to tests
iremyux Mar 21, 2026
4f3946a
Removed GetMaxCompressedLength_FitsActualOutput test
iremyux Mar 21, 2026
cd7cef9
Removed regions
iremyux Mar 21, 2026
e06c1d0
Remove Encoder_WithOptions test
iremyux Mar 21, 2026
e66a00b
Fix windowlog=8 failure for gzip and deflate
iremyux Mar 21, 2026
89510f8
Address review feedback on tests
iremyux Mar 21, 2026
8c07952
Fix tests
iremyux Mar 21, 2026
7fd0591
Use NoCompressionMemLevel for NoCompression level
iremyux Mar 21, 2026
b7b29ac
Rename ZlibEncoderDecoderTests.cs to ZLibEncoderDecoderTests.cs
iremyux Mar 21, 2026
a75b24f
Remove extra tests
iremyux Mar 22, 2026
b795f31
Add ResolveWindowBits helper method
iremyux Mar 24, 2026
f4b24c2
Add CompressionNative_CompressBound to callhelpers-pinvoke.cpp
iremyux Mar 25, 2026
94c95dc
Simplify errorCode switch
iremyux Mar 25, 2026
9b39f4d
Update src/libraries/System.IO.Compression/src/System/IO/Compression/…
iremyux Mar 25, 2026
c415ebd
REmove whitespaces
iremyux Apr 9, 2026
ba6f28a
Move ZLibEncoderDecoderTestBase to System.IO.Compression tests
iremyux Apr 9, 2026
9522265
Move ResolveWindowBits to new CompressionFormatHelper class
iremyux Apr 13, 2026
a425e1f
Switch to manual computation inside GetMaxCompressedLength
iremyux Apr 13, 2026
2d6ac86
Add test to verify our manual computation for GetMaxCompressedLength
iremyux Apr 13, 2026
f36746f
Undo wasm/callhelpers-pinvoke.cpp addition
iremyux Apr 13, 2026
bb27a9c
Revert "Undo wasm/callhelpers-pinvoke.cpp addition"
iremyux Apr 13, 2026
b976f98
Revert "Add CompressionNative_CompressBound to callhelpers-pinvoke.cpp"
iremyux Apr 13, 2026
627cb05
Use ulong arithmetic in GetMaxCompressedLength to prevent overflow
iremyux Apr 14, 2026
125cc24
Add check to Gzip's GetMaxCompressedLength
iremyux Apr 14, 2026
46c3a18
Add comments for windowlog=8
iremyux Apr 14, 2026
6e85384
Set bytesWritten = 0 when the Compress/Decompress returns falls in Tr…
iremyux Apr 15, 2026
0058b79
Add Is64BitProcess check into MaxWindowLog in ZstandardEncoderDecoder…
iremyux Apr 15, 2026
13b2aaa
Add platform condition to GetMaxCompressedLength_MatchesNativeCompres…
iremyux Apr 15, 2026
87ed0cb
Merge branch 'main' into 62113-zlib-encoder-decoder
iremyux Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libraries/Common/src/Interop/Interop.zlib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,8 @@ internal static unsafe partial ZLibNative.ErrorCode DeflateInit2_(

[LibraryImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_Crc32")]
internal static unsafe partial uint crc32(uint crc, byte* buffer, int len);

[LibraryImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_CompressBound")]
internal static partial uint compressBound(uint sourceLen);
Comment thread
iremyux marked this conversation as resolved.
Comment thread
iremyux marked this conversation as resolved.
}
}
35 changes: 35 additions & 0 deletions src/libraries/Common/src/System/IO/Compression/ZLibNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,41 @@ public enum CompressionMethod : int
/// </summary>
public const int GZip_DefaultWindowBits = 31;

/// <summary>
/// The minimum value for the base-2 logarithm of the history buffer (window) size.
/// A value of 8 corresponds to a 256-byte window.
/// </summary>
public const int MinWindowLog = 8;

/// <summary>
/// The maximum value for the base-2 logarithm of the history buffer (window) size.
/// A value of 15 corresponds to a 32KB window, which provides the best compression ratio.
/// </summary>
public const int MaxWindowLog = 15;

/// <summary>
/// The default value for the base-2 logarithm of the history buffer (window) size.
/// Defaults to <see cref="MaxWindowLog"/> (15) for optimal compression.
/// </summary>
public const int DefaultWindowLog = MaxWindowLog;

/// <summary>
/// The minimum compression quality level. A value of 0 means no compression (store only).
/// </summary>
public const int MinQuality = 0;

/// <summary>
/// The maximum compression quality level. A value of 9 provides the best compression ratio
/// but is the slowest.
/// </summary>
public const int MaxQuality = 9;

/// <summary>
/// The default compression quality level. A value of 6 provides a good balance between
/// compression ratio and speed.
/// </summary>
public const int DefaultQuality = 6;

/// <summary>
/// <para><strong>From the ZLib manual:</strong><br />
/// The <c>memLevel</c> parameter specifies how much memory should be allocated for the internal compression state.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,36 @@ private async Task TestStreamRewindsOnlyOnce(bool useAsync)
}
}

[Theory]
[InlineData(8)]
[InlineData(10)]
[InlineData(15)]
[InlineData(-1)]
public void RoundTrip_WithWindowLog(int windowLog)
{
byte[] input = new byte[1024];
Random.Shared.NextBytes(input);

var options = new ZLibCompressionOptions
{
CompressionLevel = 6,
WindowLog = windowLog
};

using var compressed = new MemoryStream();
using (var compressor = CreateStream(compressed, options, leaveOpen: true))
{
compressor.Write(input);
}

compressed.Position = 0;
using var decompressor = CreateStream(compressed, CompressionMode.Decompress);
using var decompressed = new MemoryStream();
decompressor.CopyTo(decompressed);

Assert.Equal(input, decompressed.ToArray());
}

}

public enum TestScenario
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public abstract class EncoderDecoderTestBase

protected abstract int ValidQuality { get; }
protected abstract int ValidWindowLog { get; }
protected abstract int MinQuality { get; }
protected abstract int MaxQuality { get; }
protected abstract int MinWindowLog { get; }
protected abstract int MaxWindowLog { get; }
protected abstract int InvalidQualityTooLow { get; }
protected abstract int InvalidQualityTooHigh { get; }
protected abstract int InvalidWindowLogTooLow { get; }
Expand Down Expand Up @@ -262,20 +266,14 @@ public void TryDecompress_WithEmptyDestination_ReturnsFalse()
Assert.Equal(0, bytesWritten);
}

[Theory]
[MemberData(nameof(BooleanTestData))]
public void TryDecompress_RandomData_ReturnsFalse(bool useDictionary)
[Fact]
public void TryDecompress_InvalidData_ReturnsFalse()
{
if (useDictionary && !SupportsDictionaries)
return;

// 0xFF is an invalid first byte for all supported compression formats.
Comment thread
iremyux marked this conversation as resolved.
Span<byte> source = new byte[100];
source.Fill(0xFF);
Span<byte> destination = new byte[5 * source.Length];
Comment thread
iremyux marked this conversation as resolved.

// deterministic random data that should not match any valid compressed format
Random rng = new Random(42);
rng.NextBytes(source);

Assert.False(TryDecompress(source, destination, out int bytesWritten), "TryDecompress completed successfully but should have failed");
Assert.Equal(0, bytesWritten);
}
Expand Down Expand Up @@ -664,6 +662,56 @@ public void RoundTrip_Chunks()
}
}

[Fact]
public void RoundTrip_AllCompressionLevels()
{
byte[] input = CreateTestData();

for (int quality = MinQuality; quality <= MaxQuality; quality++)
{
byte[] compressed = new byte[GetMaxCompressedLength(input.Length)];
using var encoder = CreateEncoder(quality, ValidWindowLog);
OperationStatus compressStatus = encoder.Compress(input, compressed, out int bytesConsumed, out int compressedSize, isFinalBlock: true);

Assert.Equal(OperationStatus.Done, compressStatus);
Assert.Equal(input.Length, bytesConsumed);

byte[] decompressed = new byte[input.Length];
using var decoder = CreateDecoder();
OperationStatus decompressStatus = decoder.Decompress(compressed.AsSpan(0, compressedSize), decompressed, out int decompressConsumed, out int decompressWritten);

Assert.Equal(OperationStatus.Done, decompressStatus);
Assert.Equal(compressedSize, decompressConsumed);
Assert.Equal(input.Length, decompressWritten);
Assert.Equal(input, decompressed);
}
}

[Fact]
public void RoundTrip_AllWindowLogs()
{
byte[] input = CreateTestData();

for (int windowLog = MinWindowLog; windowLog <= MaxWindowLog; windowLog++)
{
byte[] compressed = new byte[GetMaxCompressedLength(input.Length)];
using var encoder = CreateEncoder(ValidQuality, windowLog);
OperationStatus compressStatus = encoder.Compress(input, compressed, out int bytesConsumed, out int compressedSize, isFinalBlock: true);

Assert.Equal(OperationStatus.Done, compressStatus);
Assert.Equal(input.Length, bytesConsumed);

byte[] decompressed = new byte[input.Length];
using var decoder = CreateDecoder();
OperationStatus decompressStatus = decoder.Decompress(compressed.AsSpan(0, compressedSize), decompressed, out int decompressConsumed, out int decompressWritten);

Assert.Equal(OperationStatus.Done, decompressStatus);
Assert.Equal(compressedSize, decompressConsumed);
Assert.Equal(input.Length, decompressWritten);
Assert.Equal(input, decompressed);
}
}

public static byte[] CreateTestData(int size = 1000)
{
// Create test data of specified size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ public class BrotliEncoderDecoderTests : EncoderDecoderTestBase

protected override int ValidQuality => 3;
protected override int ValidWindowLog => 10;

protected override int MinQuality => 0;
protected override int MaxQuality => 11;
protected override int MinWindowLog => 10;
protected override int MaxWindowLog => 24;
protected override int InvalidQualityTooLow => -1;
protected override int InvalidQualityTooHigh => 12;
protected override int InvalidWindowLogTooLow => 9;
Expand Down
67 changes: 67 additions & 0 deletions src/libraries/System.IO.Compression/ref/System.IO.Compression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@ public enum CompressionMode
Decompress = 0,
Compress = 1,
}
public sealed partial class DeflateDecoder : System.IDisposable
{
public DeflateDecoder() { }
public System.Buffers.OperationStatus Decompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesConsumed, out int bytesWritten) { throw null; }
public void Dispose() { }
public static bool TryDecompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten) { throw null; }
}
public sealed partial class DeflateEncoder : System.IDisposable
{
public DeflateEncoder() { }
public DeflateEncoder(int quality) { }
public DeflateEncoder(int quality, int windowLog) { }
public DeflateEncoder(System.IO.Compression.ZLibCompressionOptions options) { }
public System.Buffers.OperationStatus Compress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) { throw null; }
public void Dispose() { }
public System.Buffers.OperationStatus Flush(System.Span<byte> destination, out int bytesWritten) { throw null; }
public static long GetMaxCompressedLength(long inputLength) { throw null; }
Comment thread
iremyux marked this conversation as resolved.
public static bool TryCompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten) { throw null; }
public static bool TryCompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten, int quality) { throw null; }
public static bool TryCompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten, int quality, int windowLog) { throw null; }
}
public partial class DeflateStream : System.IO.Stream
{
public DeflateStream(System.IO.Stream stream, System.IO.Compression.CompressionLevel compressionLevel) { }
Expand Down Expand Up @@ -54,6 +75,27 @@ public override void Write(System.ReadOnlySpan<byte> buffer) { }
public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory<byte> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override void WriteByte(byte value) { }
}
public sealed partial class GZipDecoder : System.IDisposable
{
public GZipDecoder() { }
public System.Buffers.OperationStatus Decompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesConsumed, out int bytesWritten) { throw null; }
public void Dispose() { }
public static bool TryDecompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten) { throw null; }
}
public sealed partial class GZipEncoder : System.IDisposable
{
public GZipEncoder() { }
public GZipEncoder(int quality) { }
public GZipEncoder(int quality, int windowLog) { }
public GZipEncoder(System.IO.Compression.ZLibCompressionOptions options) { }
public System.Buffers.OperationStatus Compress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) { throw null; }
public void Dispose() { }
public System.Buffers.OperationStatus Flush(System.Span<byte> destination, out int bytesWritten) { throw null; }
public static long GetMaxCompressedLength(long inputLength) { throw null; }
public static bool TryCompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten) { throw null; }
public static bool TryCompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten, int quality) { throw null; }
public static bool TryCompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten, int quality, int windowLog) { throw null; }
}
public partial class GZipStream : System.IO.Stream
{
public GZipStream(System.IO.Stream stream, System.IO.Compression.CompressionLevel compressionLevel) { }
Expand Down Expand Up @@ -146,9 +188,13 @@ public enum ZipCompressionMethod
}
public sealed partial class ZLibCompressionOptions
{
public static int DefaultWindowLog { get { throw null; } }
public static int MaxWindowLog { get { throw null; } }
public static int MinWindowLog { get { throw null; } }
public ZLibCompressionOptions() { }
public int CompressionLevel { get { throw null; } set { } }
public System.IO.Compression.ZLibCompressionStrategy CompressionStrategy { get { throw null; } set { } }
public int WindowLog { get { throw null; } set { } }
}
public enum ZLibCompressionStrategy
{
Expand All @@ -158,6 +204,27 @@ public enum ZLibCompressionStrategy
RunLengthEncoding = 3,
Fixed = 4,
}
public sealed partial class ZLibDecoder : System.IDisposable
{
public ZLibDecoder() { }
public System.Buffers.OperationStatus Decompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesConsumed, out int bytesWritten) { throw null; }
public void Dispose() { }
public static bool TryDecompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten) { throw null; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No bytes consumed info?
With zlib framing you sometimes really need that info to avoid skipping data.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does additional data cause false or is it silently ignored?

}
public sealed partial class ZLibEncoder : System.IDisposable
{
public ZLibEncoder() { }
public ZLibEncoder(int quality) { }
public ZLibEncoder(int quality, int windowLog) { }
public ZLibEncoder(System.IO.Compression.ZLibCompressionOptions options) { }
public System.Buffers.OperationStatus Compress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) { throw null; }
public void Dispose() { }
public System.Buffers.OperationStatus Flush(System.Span<byte> destination, out int bytesWritten) { throw null; }
public static long GetMaxCompressedLength(long inputLength) { throw null; }
public static bool TryCompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten) { throw null; }
public static bool TryCompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten, int quality) { throw null; }
public static bool TryCompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten, int quality, int windowLog) { throw null; }
}
public sealed partial class ZLibStream : System.IO.Stream
{
public ZLibStream(System.IO.Stream stream, System.IO.Compression.CompressionLevel compressionLevel) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,19 @@
Link="Common\System\IO\Compression\ZLibNative.CompressionLevel.cs" />
<Compile Include="$(CommonPath)System\IO\Compression\ZLibNative.ZStream.cs"
Link="Common\System\IO\Compression\ZLibNative.ZStream.cs" />
<Compile Include="System\IO\Compression\CompressionFormat.cs" />
<Compile Include="System\IO\Compression\CompressionLevel.cs" />
<Compile Include="System\IO\Compression\CompressionMode.cs" />
<Compile Include="System\IO\Compression\Crc32Helper.ZLib.cs" />
<Compile Include="System\IO\Compression\GZipStream.cs" />
<Compile Include="System\IO\Compression\PositionPreservingWriteOnlyStreamWrapper.cs" />
<Compile Include="System\IO\Compression\DeflateDecoder.cs" />
<Compile Include="System\IO\Compression\DeflateEncoder.cs" />
<Compile Include="System\IO\Compression\GZipDecoder.cs" />
<Compile Include="System\IO\Compression\GZipEncoder.cs" />
<Compile Include="System\IO\Compression\ZLibCompressionOptions.cs" />
<Compile Include="System\IO\Compression\ZLibDecoder.cs" />
<Compile Include="System\IO\Compression\ZLibEncoder.cs" />
<Compile Include="System\IO\Compression\ZLibStream.cs" />
<Compile Include="System\IO\Compression\ZipCompressionMethod.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.IO.Compression
{
/// <summary>
/// Specifies the compression format for zlib-based encoders.
/// </summary>
internal enum CompressionFormat
{
/// <summary>Raw deflate format (no header/trailer).</summary>
Deflate,
/// <summary>ZLib format (zlib header/trailer).</summary>
ZLib,
/// <summary>GZip format (gzip header/trailer).</summary>
GZip
}

internal static class CompressionFormatHelper
{
/// <summary>
/// Resolves a windowLog value (8-15) to the windowBits parameter expected by zlib,
/// based on the compression format. A windowLog of -1 is resolved to the default (15).
/// </summary>
/// <remarks>
/// zlib-ng rejects windowBits 8 for raw deflate and gzip; classic zlib silently upgrades to 9.
/// This method clamps to 9 for Deflate and GZip formats to match classic zlib behavior.
/// </remarks>
internal static int ResolveWindowBits(int windowLog, CompressionFormat format)
{
if (windowLog == -1)
{
windowLog = ZLibNative.DefaultWindowLog;
}

if (format != CompressionFormat.ZLib)
{
windowLog = Math.Max(windowLog, 9);
}

return format switch
{
CompressionFormat.Deflate => -windowLog,
CompressionFormat.ZLib => windowLog,
CompressionFormat.GZip => windowLog + 16,
_ => throw new ArgumentOutOfRangeException(nameof(format))
};
}
}
}
Loading
Loading