From e2060317ff55e8706b00c9279f3c51d4364c823f Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 25 Jul 2024 14:54:15 -0700 Subject: [PATCH] Apply feedbacks --- .../Compression/CompressionStreamTestBase.cs | 12 ++- .../CompressionStreamUnitTestBase.cs | 69 +++++++++++++++-- .../Compression/enc/BrotliStream.Compress.cs | 5 ++ .../CompressionStreamUnitTests.Brotli.cs | 74 ++++++------------- .../Compression/DeflateZLib/DeflateStream.cs | 42 +++++------ .../IO/Compression/DeflateZLib/Deflater.cs | 58 +-------------- .../src/System/IO/Compression/GZipStream.cs | 1 + .../src/System/IO/Compression/ZLibStream.cs | 1 + .../CompressionStreamUnitTests.Deflate.cs | 70 +++--------------- .../tests/CompressionStreamUnitTests.Gzip.cs | 69 +++-------------- .../tests/CompressionStreamUnitTests.ZLib.cs | 71 +++--------------- 11 files changed, 150 insertions(+), 322 deletions(-) diff --git a/src/libraries/Common/tests/System/IO/Compression/CompressionStreamTestBase.cs b/src/libraries/Common/tests/System/IO/Compression/CompressionStreamTestBase.cs index 466671a791a363..791e688e3891d3 100644 --- a/src/libraries/Common/tests/System/IO/Compression/CompressionStreamTestBase.cs +++ b/src/libraries/Common/tests/System/IO/Compression/CompressionStreamTestBase.cs @@ -27,9 +27,16 @@ public static IEnumerable UncompressedTestFiles() yield return new object[] { Path.Combine("UncompressedTestFiles", "sum") }; yield return new object[] { Path.Combine("UncompressedTestFiles", "xargs.1") }; } + public static IEnumerable UncompressedTestFilesZLib() + { + yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.doc") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.docx") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.pdf") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "sum") }; + } public static IEnumerable ZLibOptionsRoundTripTestData() { - yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.doc"), new ZLibCompressionOptions() { CompressionLevel = -1, CompressionStrategy = ZLibCompressionStrategy.Default } }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.doc"), new ZLibCompressionOptions() { CompressionLevel = 0, CompressionStrategy = ZLibCompressionStrategy.Default } }; yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.docx"), new ZLibCompressionOptions() { CompressionLevel = 3, CompressionStrategy = ZLibCompressionStrategy.Filtered } }; yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.pdf"), new ZLibCompressionOptions() { CompressionLevel = 5, CompressionStrategy = ZLibCompressionStrategy.RunLengthEncoding } }; yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.txt"), new ZLibCompressionOptions() { CompressionLevel = 7, CompressionStrategy = ZLibCompressionStrategy.HuffmanOnly } }; @@ -38,7 +45,7 @@ public static IEnumerable ZLibOptionsRoundTripTestData() yield return new object[] { Path.Combine("UncompressedTestFiles", "cp.html"), new ZLibCompressionOptions() { CompressionLevel = 4, CompressionStrategy = ZLibCompressionStrategy.Default } }; yield return new object[] { Path.Combine("UncompressedTestFiles", "fields.c"), new ZLibCompressionOptions() { CompressionLevel = 6, CompressionStrategy = ZLibCompressionStrategy.HuffmanOnly } }; yield return new object[] { Path.Combine("UncompressedTestFiles", "grammar.lsp"), new ZLibCompressionOptions() { CompressionLevel = 8, CompressionStrategy = ZLibCompressionStrategy.Default } }; - yield return new object[] { Path.Combine("UncompressedTestFiles", "kennedy.xls"), new ZLibCompressionOptions() { CompressionLevel = -1, CompressionStrategy = ZLibCompressionStrategy.Fixed } }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "kennedy.xls"), new ZLibCompressionOptions() { CompressionLevel = 1, CompressionStrategy = ZLibCompressionStrategy.Fixed } }; yield return new object[] { Path.Combine("UncompressedTestFiles", "lcet10.txt"), new ZLibCompressionOptions() { CompressionLevel = 1, CompressionStrategy = ZLibCompressionStrategy.Filtered } }; yield return new object[] { Path.Combine("UncompressedTestFiles", "plrabn12.txt"), new ZLibCompressionOptions() { CompressionLevel = 2, CompressionStrategy = ZLibCompressionStrategy.RunLengthEncoding } }; yield return new object[] { Path.Combine("UncompressedTestFiles", "ptt5"), new ZLibCompressionOptions() { CompressionLevel = 3, CompressionStrategy = ZLibCompressionStrategy.Default } }; @@ -55,6 +62,7 @@ public abstract class CompressionStreamTestBase : CompressionTestBase public abstract Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen); public abstract Stream CreateStream(Stream stream, CompressionLevel level); public abstract Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen); + public abstract Stream CreateStream(Stream stream, ZLibCompressionOptions options, bool leaveOpen); public abstract Stream BaseStream(Stream stream); public virtual int BufferSize { get => 8192; } diff --git a/src/libraries/Common/tests/System/IO/Compression/CompressionStreamUnitTestBase.cs b/src/libraries/Common/tests/System/IO/Compression/CompressionStreamUnitTestBase.cs index 40fd054f473488..5f63364d8a7254 100644 --- a/src/libraries/Common/tests/System/IO/Compression/CompressionStreamUnitTestBase.cs +++ b/src/libraries/Common/tests/System/IO/Compression/CompressionStreamUnitTestBase.cs @@ -369,13 +369,14 @@ public async Task TestLeaveOpenAfterValidDecompress() [Fact] public void Ctor_ArgumentValidation() { - Assert.Throws(() => CreateStream(null, CompressionLevel.Fastest)); - Assert.Throws(() => CreateStream(null, CompressionMode.Decompress)); - Assert.Throws(() => CreateStream(null, CompressionMode.Compress)); + Assert.Throws("stream", () => CreateStream(null, CompressionLevel.Fastest)); + Assert.Throws("stream", () => CreateStream(null, CompressionMode.Decompress)); + Assert.Throws("stream", () => CreateStream(null, CompressionMode.Compress)); - Assert.Throws(() => CreateStream(null, CompressionLevel.Fastest, true)); - Assert.Throws(() => CreateStream(null, CompressionMode.Decompress, false)); - Assert.Throws(() => CreateStream(null, CompressionMode.Compress, true)); + Assert.Throws("stream", () => CreateStream(null, CompressionLevel.Fastest, true)); + Assert.Throws("stream", () => CreateStream(null, CompressionMode.Decompress, false)); + Assert.Throws("stream", () => CreateStream(null, CompressionMode.Compress, true)); + Assert.Throws("compressionOptions", () => CreateStream(new MemoryStream(), null, true)); AssertExtensions.Throws("mode", () => CreateStream(new MemoryStream(), (CompressionMode)42)); AssertExtensions.Throws("mode", () => CreateStream(new MemoryStream(), (CompressionMode)43, true)); @@ -471,7 +472,7 @@ public async Task BaseStream_ValidAfterDisposeWithTrueLeaveOpen(CompressionMode } [Theory] - [MemberData(nameof(UncompressedTestFiles))] + [MemberData(nameof(UncompressedTestFilesZLib))] public async Task CompressionLevel_SizeInOrder(string testFile) { using var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); @@ -493,7 +494,59 @@ async Task GetLengthAsync(CompressionLevel compressionLevel) Assert.True(noCompressionLength >= fastestLength); Assert.True(fastestLength >= optimalLength); - // Assert.True(optimalLength >= smallestLength); // for some files this condition is failing (cp.html, grammar.lsp, xargs.1) + Assert.True(optimalLength >= smallestLength); + } + + [Theory] + [MemberData(nameof(ZLibOptionsRoundTripTestData))] + public async Task RoundTripWithZLibCompressionOptions(string testFile, ZLibCompressionOptions options) + { + var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); + var compressedStream = CompressTestFile(uncompressedStream, options); + using var decompressor = CreateStream(compressedStream, mode: CompressionMode.Decompress); + var decompressorOutput = new MemoryStream(); + int _bufferSize = 1024; + var bytes = new byte[_bufferSize]; + bool finished = false; + int retCount; + while (!finished) + { + retCount = await decompressor.ReadAsync(bytes, 0, _bufferSize); + + if (retCount != 0) + await decompressorOutput.WriteAsync(bytes, 0, retCount); + else + finished = true; + } + decompressor.Dispose(); + decompressorOutput.Position = 0; + uncompressedStream.Position = 0; + + byte[] uncompressedStreamBytes = uncompressedStream.ToArray(); + byte[] decompressorOutputBytes = decompressorOutput.ToArray(); + + Assert.Equal(uncompressedStreamBytes.Length, decompressorOutputBytes.Length); + for (int i = 0; i < uncompressedStreamBytes.Length; i++) + { + Assert.Equal(uncompressedStreamBytes[i], decompressorOutputBytes[i]); + } + } + + private MemoryStream CompressTestFile(LocalMemoryStream testStream, ZLibCompressionOptions options) + { + var compressorOutput = new MemoryStream(); + using (var compressionStream = CreateStream(compressorOutput, options, leaveOpen: true)) + { + var buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = testStream.Read(buffer, 0, buffer.Length)) > 0) + { + compressionStream.Write(buffer, 0, bytesRead); + } + } + + compressorOutput.Position = 0; + return compressorOutput; } } diff --git a/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliStream.Compress.cs b/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliStream.Compress.cs index 4ace04147c3123..4ae7190e9ab00b 100644 --- a/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliStream.Compress.cs +++ b/src/libraries/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliStream.Compress.cs @@ -16,12 +16,14 @@ public sealed partial class BrotliStream : Stream /// Initializes a new instance of the class by using the specified stream and compression level. /// The stream to which compressed data is written. /// One of the enumeration values that indicates whether to emphasize speed or compression efficiency when compressing data to the stream. + /// if is . public BrotliStream(Stream stream, CompressionLevel compressionLevel) : this(stream, compressionLevel, leaveOpen: false) { } /// Initializes a new instance of the class by using the specified stream and compression level, and optionally leaves the stream open. /// The stream to which compressed data is written. /// One of the enumeration values that indicates whether to emphasize speed or compression efficiency when compressing data to the stream. /// to leave the stream open after disposing the object; otherwise, . + /// if is . public BrotliStream(Stream stream, CompressionLevel compressionLevel, bool leaveOpen) : this(stream, CompressionMode.Compress, leaveOpen) { _encoder.SetQuality(BrotliUtils.GetQualityFromCompressionLevel(compressionLevel)); @@ -33,8 +35,11 @@ public BrotliStream(Stream stream, CompressionLevel compressionLevel, bool leave /// The stream to which compressed data is written. /// The Brotli options for fine tuning the compression stream. /// to leave the stream open after disposing the object; otherwise, . + /// if or is . public BrotliStream(Stream stream, BrotliCompressionOptions compressionOptions, bool leaveOpen = false) : this(stream, CompressionMode.Compress, leaveOpen) { + ArgumentNullException.ThrowIfNull(compressionOptions); + _encoder.SetQuality(compressionOptions.Quality); } diff --git a/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs b/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs index 13e0fc502f9055..dfcd7807568f6f 100644 --- a/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs +++ b/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs @@ -15,10 +15,25 @@ public class BrotliStreamUnitTests : CompressionStreamUnitTestBase public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new BrotliStream(stream, mode, leaveOpen); public override Stream CreateStream(Stream stream, CompressionLevel level) => new BrotliStream(stream, level); public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new BrotliStream(stream, level, leaveOpen); + public override Stream CreateStream(Stream stream, ZLibCompressionOptions options, bool leaveOpen) => + new BrotliStream(stream, options == null ? null : new BrotliCompressionOptions() { Quality = options.CompressionLevel }, leaveOpen); public override Stream BaseStream(Stream stream) => ((BrotliStream)stream).BaseStream; protected override bool FlushGuaranteesAllDataWritten => false; + public static IEnumerable UncompressedTestFilesBrotli() + { + yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "alice29.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "asyoulik.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "cp.html") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "fields.c") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "lcet10.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "plrabn12.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "sum") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "xargs.1") }; + } + // The tests are relying on an implementation detail of BrotliStream, using knowledge of its internal buffer size // in various test calculations. Currently the implementation is using the ArrayPool, which will round up to a // power-of-2. If the buffer size employed changes (which could also mean that ArrayPool.Shared starts giving @@ -77,55 +92,8 @@ public void InvalidBrotliCompressionQuality() Assert.Throws("value", () => options.Quality = 12); } - public static IEnumerable BrotliOptionsRoundTripTestData() - { - yield return new object[] { 1000, new BrotliCompressionOptions() { Quality = 0 } }; - yield return new object[] { 900, new BrotliCompressionOptions() { Quality = 3 } }; - yield return new object[] { 1200, new BrotliCompressionOptions() { Quality = 5 } }; - yield return new object[] { 2000, new BrotliCompressionOptions() { Quality = 6 } }; - yield return new object[] { 3000, new BrotliCompressionOptions() { Quality = 2 } }; - yield return new object[] { 1500, new BrotliCompressionOptions() { Quality = 7 } }; - yield return new object[] { 500, new BrotliCompressionOptions() { Quality = 9 } }; - yield return new object[] { 1000, new BrotliCompressionOptions() { Quality = 11 } }; - } - [Theory] - [MemberData(nameof(BrotliOptionsRoundTripTestData))] - public static void Roundtrip_WriteByte_ReadByte_DifferentQuality_Success(int totalLength, BrotliCompressionOptions options) - { - byte[] correctUncompressedBytes = Enumerable.Range(0, totalLength).Select(i => (byte)i).ToArray(); - - byte[] compressedBytes; - using (var ms = new MemoryStream()) - { - var bs = new BrotliStream(ms, options); - foreach (byte b in correctUncompressedBytes) - { - bs.WriteByte(b); - } - bs.Dispose(); - compressedBytes = ms.ToArray(); - } - - byte[] decompressedBytes = new byte[correctUncompressedBytes.Length]; - using (var ms = new MemoryStream(compressedBytes)) - using (var bs = new BrotliStream(ms, CompressionMode.Decompress)) - { - for (int i = 0; i < decompressedBytes.Length; i++) - { - int b = bs.ReadByte(); - Assert.InRange(b, 0, 255); - decompressedBytes[i] = (byte)b; - } - Assert.Equal(-1, bs.ReadByte()); - Assert.Equal(-1, bs.ReadByte()); - } - - Assert.Equal(correctUncompressedBytes, decompressedBytes); - } - - [Theory] - [MemberData(nameof(UncompressedTestFiles))] + [MemberData(nameof(UncompressedTestFilesBrotli))] public async void BrotliCompressionQuality_SizeInOrder(string testFile) { using var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); @@ -156,11 +124,11 @@ async Task GetLengthAsync(int compressionQuality) Assert.True(quality1 <= quality0); Assert.True(quality2 <= quality1); - Assert.True(quality3 <= quality1); - Assert.True(quality4 <= quality2); - Assert.True(quality5 <= quality1); - Assert.True(quality6 <= quality1); - Assert.True(quality7 <= quality4); + Assert.True(quality3 <= quality2); + Assert.True(quality4 <= quality3); + Assert.True(quality5 <= quality4); + Assert.True(quality6 <= quality5); + Assert.True(quality7 <= quality6); Assert.True(quality8 <= quality7); Assert.True(quality9 <= quality8); Assert.True(quality10 <= quality9); diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs index 619a770ed77d23..cb79e2e5f75bcb 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using static System.IO.Compression.ZLibNative; namespace System.IO.Compression { @@ -51,6 +52,7 @@ public DeflateStream(Stream stream, CompressionLevel compressionLevel, bool leav /// The stream to which compressed data is written. /// The options for fine tuning the compression stream. /// to leave the stream object open after disposing the object; otherwise, + /// or is . public DeflateStream(Stream stream, ZLibCompressionOptions compressionOptions, bool leaveOpen = false) : this(stream, compressionOptions, leaveOpen, ZLibNative.Deflate_DefaultWindowBits) { } @@ -58,23 +60,9 @@ public DeflateStream(Stream stream, ZLibCompressionOptions compressionOptions, b internal DeflateStream(Stream stream, ZLibCompressionOptions compressionOptions, bool leaveOpen, int windowBits) { ArgumentNullException.ThrowIfNull(stream); + ArgumentNullException.ThrowIfNull(compressionOptions); - _stream = stream; - _mode = CompressionMode.Compress; - _leaveOpen = leaveOpen; - InitializeDeflater(stream, compressionOptions, windowBits); - } - - internal void InitializeDeflater(Stream stream, ZLibCompressionOptions compressionOptions, int windowBits) - { - Debug.Assert(stream != null); - if (!stream.CanWrite) - { - throw new ArgumentException(SR.NotSupported_UnwritableStream, nameof(stream)); - } - - _deflater = new Deflater(compressionOptions, windowBits); - InitializeBuffer(); + InitializeDeflater(stream, (ZLibNative.CompressionLevel)compressionOptions.CompressionLevel, (CompressionStrategy)compressionOptions.CompressionStrategy, leaveOpen, windowBits); } /// @@ -98,7 +86,7 @@ internal DeflateStream(Stream stream, CompressionMode mode, bool leaveOpen, int break; case CompressionMode.Compress: - InitializeDeflater(stream, leaveOpen, windowBits, CompressionLevel.Optimal); + InitializeDeflater(stream, ZLibNative.CompressionLevel.DefaultCompression, CompressionStrategy.DefaultStrategy, leaveOpen, windowBits); break; default: @@ -107,26 +95,26 @@ internal DeflateStream(Stream stream, CompressionMode mode, bool leaveOpen, int } /// - /// Internal constructor to specify the compressionlevel as well as the windowbits + /// Internal constructor to specify the compressionLevel as well as the windowBits /// internal DeflateStream(Stream stream, CompressionLevel compressionLevel, bool leaveOpen, int windowBits) { ArgumentNullException.ThrowIfNull(stream); - InitializeDeflater(stream, leaveOpen, windowBits, compressionLevel); + InitializeDeflater(stream, GetZLibNativeCompressionLevel(compressionLevel), CompressionStrategy.DefaultStrategy, leaveOpen, windowBits); } /// /// Sets up this DeflateStream to be used for Zlib Deflation/Compression /// [MemberNotNull(nameof(_stream))] - internal void InitializeDeflater(Stream stream, bool leaveOpen, int windowBits, CompressionLevel compressionLevel) + internal void InitializeDeflater(Stream stream, ZLibNative.CompressionLevel compressionLevel, CompressionStrategy strategy, bool leaveOpen, int windowBits) { Debug.Assert(stream != null); if (!stream.CanWrite) throw new ArgumentException(SR.NotSupported_UnwritableStream, nameof(stream)); - _deflater = new Deflater(compressionLevel, windowBits); + _deflater = new Deflater(compressionLevel, strategy, windowBits, GetMemLevel(compressionLevel)); _stream = stream; _mode = CompressionMode.Compress; @@ -134,6 +122,18 @@ internal void InitializeDeflater(Stream stream, bool leaveOpen, int windowBits, InitializeBuffer(); } + private static ZLibNative.CompressionLevel GetZLibNativeCompressionLevel(CompressionLevel compressionLevel) => + compressionLevel switch + { + CompressionLevel.Optimal => ZLibNative.CompressionLevel.DefaultCompression, + CompressionLevel.Fastest => ZLibNative.CompressionLevel.BestSpeed, + CompressionLevel.NoCompression => ZLibNative.CompressionLevel.NoCompression, + CompressionLevel.SmallestSize => ZLibNative.CompressionLevel.BestCompression, + _ => throw new ArgumentOutOfRangeException(nameof(compressionLevel)), + }; + + private static int GetMemLevel(ZLibNative.CompressionLevel level) => level == ZLibNative.CompressionLevel.NoCompression ? Deflate_NoCompressionMemLevel : Deflate_DefaultMemLevel; + [MemberNotNull(nameof(_buffer))] private void InitializeBuffer() { diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs index 8fcccef7612f0a..aed2fcaca71497 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs @@ -28,26 +28,20 @@ internal sealed class Deflater : IDisposable // on the stream explicitly. private object SyncLock => this; - internal Deflater(ZLibCompressionOptions options, int windowBits) + internal Deflater(ZLibNative.CompressionLevel compressionLevel, ZLibNative.CompressionStrategy strategy, int windowBits, int memLevel) { Debug.Assert(windowBits >= minWindowBits && windowBits <= maxWindowBits); - int memLevel = options.CompressionLevel == 0 ? ZLibNative.Deflate_NoCompressionMemLevel : ZLibNative.Deflate_DefaultMemLevel; ZErrorCode errC; try { - errC = ZLibNative.CreateZLibStreamForDeflate(out _zlibStream, (ZLibNative.CompressionLevel)options.CompressionLevel, windowBits, memLevel, (ZLibNative.CompressionStrategy)options.CompressionStrategy); + errC = ZLibNative.CreateZLibStreamForDeflate(out _zlibStream, compressionLevel, windowBits, memLevel, strategy); } catch (Exception cause) { throw new ZLibException(SR.ZLibErrorDLLLoadError, cause); } - CheckErrorCode(errC); - } - - private void CheckErrorCode(ZErrorCode errC) - { switch (errC) { case ZErrorCode.Ok: @@ -67,54 +61,6 @@ private void CheckErrorCode(ZErrorCode errC) } } - - internal Deflater(CompressionLevel compressionLevel, int windowBits) - { - Debug.Assert(windowBits >= minWindowBits && windowBits <= maxWindowBits); - ZLibNative.CompressionLevel zlibCompressionLevel; - int memLevel; - - switch (compressionLevel) - { - // See the note in ZLibNative.CompressionLevel for the recommended combinations. - case CompressionLevel.Optimal: - zlibCompressionLevel = ZLibNative.CompressionLevel.DefaultCompression; - memLevel = ZLibNative.Deflate_DefaultMemLevel; - break; - - case CompressionLevel.Fastest: - zlibCompressionLevel = ZLibNative.CompressionLevel.BestSpeed; - memLevel = ZLibNative.Deflate_DefaultMemLevel; - break; - - case CompressionLevel.NoCompression: - zlibCompressionLevel = ZLibNative.CompressionLevel.NoCompression; - memLevel = ZLibNative.Deflate_NoCompressionMemLevel; - break; - - case CompressionLevel.SmallestSize: - zlibCompressionLevel = ZLibNative.CompressionLevel.BestCompression; - memLevel = ZLibNative.Deflate_DefaultMemLevel; - break; - - default: - throw new ArgumentOutOfRangeException(nameof(compressionLevel)); - } - - ZErrorCode errC; - try - { - errC = ZLibNative.CreateZLibStreamForDeflate(out _zlibStream, zlibCompressionLevel, - windowBits, memLevel, ZLibNative.CompressionStrategy.DefaultStrategy); - } - catch (Exception cause) - { - throw new ZLibException(SR.ZLibErrorDLLLoadError, cause); - } - - CheckErrorCode(errC); - } - ~Deflater() { Dispose(false); diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/GZipStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/GZipStream.cs index 68f1a3fbed4ece..086035c1ad078d 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/GZipStream.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/GZipStream.cs @@ -38,6 +38,7 @@ public GZipStream(Stream stream, CompressionLevel compressionLevel, bool leaveOp /// The stream to which compressed data is written. /// The options for fine tuning the compression stream. /// to leave the stream object open after disposing the object; otherwise, . + /// or is . public GZipStream(Stream stream, ZLibCompressionOptions compressionOptions, bool leaveOpen = false) { _deflateStream = new DeflateStream(stream, compressionOptions, leaveOpen, ZLibNative.GZip_DefaultWindowBits); diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZLibStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZLibStream.cs index 8ecc7cc3ae94fd..1a20a16b97f3a3 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZLibStream.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZLibStream.cs @@ -51,6 +51,7 @@ public ZLibStream(Stream stream, CompressionLevel compressionLevel, bool leaveOp /// The stream to which compressed data is written. /// The ZLib options for fine tuning the compression stream. /// to leave the stream object open after disposing the object; otherwise, . + /// or is . public ZLibStream(Stream stream, ZLibCompressionOptions compressionOptions, bool leaveOpen = false) { _deflateStream = new DeflateStream(stream, compressionOptions, leaveOpen, ZLibNative.ZLib_DefaultWindowBits); diff --git a/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs b/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs index 4f3454e66bc803..8de9f745aa6193 100644 --- a/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs +++ b/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections; using System.Collections.Generic; using System.IO.Compression.Tests; using System.Linq; @@ -20,6 +19,7 @@ public class DeflateStreamUnitTests : CompressionStreamUnitTestBase public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new DeflateStream(stream, mode, leaveOpen); public override Stream CreateStream(Stream stream, CompressionLevel level) => new DeflateStream(stream, level); public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new DeflateStream(stream, level, leaveOpen); + public override Stream CreateStream(Stream stream, ZLibCompressionOptions options, bool leaveOpen) => new DeflateStream(stream, options, leaveOpen); public override Stream BaseStream(Stream stream) => ((DeflateStream)stream).BaseStream; protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("DeflateTestData", Path.GetFileName(uncompressedPath)); @@ -221,60 +221,8 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati } [Theory] - [MemberData(nameof(ZLibOptionsRoundTripTestData))] - public async Task RoundTripWithOptions(string testFile, ZLibCompressionOptions options) - { - var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); - var compressedStream = CompressTestFile(uncompressedStream, options); - using var decompressor = new DeflateStream(compressedStream, mode: CompressionMode.Decompress); - var decompressorOutput = new MemoryStream(); - int _bufferSize = 1024; - var bytes = new byte[_bufferSize]; - bool finished = false; - int retCount; - while (!finished) - { - retCount = await decompressor.ReadAsync(bytes, 0, _bufferSize); - - if (retCount != 0) - await decompressorOutput.WriteAsync(bytes, 0, retCount); - else - finished = true; - } - decompressor.Dispose(); - decompressorOutput.Position = 0; - uncompressedStream.Position = 0; - - byte[] uncompressedStreamBytes = uncompressedStream.ToArray(); - byte[] decompressorOutputBytes = decompressorOutput.ToArray(); - - Assert.Equal(uncompressedStreamBytes.Length, decompressorOutputBytes.Length); - for (int i = 0; i < uncompressedStreamBytes.Length; i++) - { - Assert.Equal(uncompressedStreamBytes[i], decompressorOutputBytes[i]); - } - } - - private MemoryStream CompressTestFile(LocalMemoryStream testStream, ZLibCompressionOptions options) - { - var compressorOutput = new MemoryStream(); - using (var compressionStream = new DeflateStream(compressorOutput, options, leaveOpen: true)) - { - var buffer = new byte[4096]; - int bytesRead; - while ((bytesRead = testStream.Read(buffer, 0, buffer.Length)) > 0) - { - compressionStream.Write(buffer, 0, bytesRead); - } - } - - compressorOutput.Position = 0; - return compressorOutput; - } - - [Theory] - [MemberData(nameof(UncompressedTestFiles))] - public async void DeflateCompression_SizeInOrder(string testFile) + [MemberData(nameof(UncompressedTestFilesZLib))] + public async void ZLibCompressionLevel_SizeInOrder(string testFile) { using var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); @@ -299,15 +247,15 @@ async Task GetLengthAsync(int compressionLevel) long level8 = await GetLengthAsync(8); long level9 = await GetLengthAsync(9); - // Depending on the file type the compression level is not linearly affect the compressed size Assert.True(level1 <= level0); Assert.True(level2 <= level1); Assert.True(level3 <= level2); - Assert.True(level4 <= level2); - Assert.True(level5 <= level3); - Assert.True(level6 <= level3); - Assert.True(level8 <= level6); - Assert.True(level9 <= level7); + Assert.True(level4 <= level3); + Assert.True(level5 <= level4); + Assert.True(level6 <= level5); + Assert.True(level7 <= level6); + Assert.True(level8 <= level7); + Assert.True(level9 <= level8); } } } diff --git a/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Gzip.cs b/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Gzip.cs index 8d9ad2025eade2..c197357b12835d 100644 --- a/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Gzip.cs +++ b/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Gzip.cs @@ -18,6 +18,7 @@ public class GzipStreamUnitTests : CompressionStreamUnitTestBase public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new GZipStream(stream, mode, leaveOpen); public override Stream CreateStream(Stream stream, CompressionLevel level) => new GZipStream(stream, level); public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new GZipStream(stream, level, leaveOpen); + public override Stream CreateStream(Stream stream, ZLibCompressionOptions options, bool leaveOpen) => new GZipStream(stream, options, leaveOpen); public override Stream BaseStream(Stream stream) => ((GZipStream)stream).BaseStream; protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("GZipTestData", Path.GetFileName(uncompressedPath) + ".gz"); @@ -442,60 +443,8 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati } [Theory] - [MemberData(nameof(ZLibOptionsRoundTripTestData))] - public async Task RoundTripWithOptions(string testFile, ZLibCompressionOptions options) - { - var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); - var compressedStream = CompressTestFile(uncompressedStream, options); - using var decompressor = new GZipStream(compressedStream, mode: CompressionMode.Decompress); - var decompressorOutput = new MemoryStream(); - int _bufferSize = 1024; - var bytes = new byte[_bufferSize]; - bool finished = false; - int retCount; - while (!finished) - { - retCount = await decompressor.ReadAsync(bytes, 0, _bufferSize); - - if (retCount != 0) - await decompressorOutput.WriteAsync(bytes, 0, retCount); - else - finished = true; - } - decompressor.Dispose(); - decompressorOutput.Position = 0; - uncompressedStream.Position = 0; - - byte[] uncompressedStreamBytes = uncompressedStream.ToArray(); - byte[] decompressorOutputBytes = decompressorOutput.ToArray(); - - Assert.Equal(uncompressedStreamBytes.Length, decompressorOutputBytes.Length); - for (int i = 0; i < uncompressedStreamBytes.Length; i++) - { - Assert.Equal(uncompressedStreamBytes[i], decompressorOutputBytes[i]); - } - } - - private MemoryStream CompressTestFile(LocalMemoryStream testStream, ZLibCompressionOptions options) - { - var compressorOutput = new MemoryStream(); - using (var compressionStream = new GZipStream(compressorOutput, options, leaveOpen: true)) - { - var buffer = new byte[4096]; - int bytesRead; - while ((bytesRead = testStream.Read(buffer, 0, buffer.Length)) > 0) - { - compressionStream.Write(buffer, 0, bytesRead); - } - } - - compressorOutput.Position = 0; - return compressorOutput; - } - - [Theory] - [MemberData(nameof(UncompressedTestFiles))] - public async void GZipCompression_SizeInOrder(string testFile) + [MemberData(nameof(UncompressedTestFilesZLib))] + public async void ZLibCompressionLevel_SizeInOrder(string testFile) { using var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); @@ -520,15 +469,15 @@ async Task GetLengthAsync(int compressionLevel) long level8 = await GetLengthAsync(8); long level9 = await GetLengthAsync(9); - // Depending on the file type the higher compression level is not always produce higher compressed size, especially when the levels are close. Assert.True(level1 <= level0); Assert.True(level2 <= level1); Assert.True(level3 <= level2); - Assert.True(level4 <= level2); - Assert.True(level5 <= level3); - Assert.True(level6 <= level3); - Assert.True(level8 <= level6); - Assert.True(level9 <= level4); + Assert.True(level4 <= level3); + Assert.True(level5 <= level4); + Assert.True(level6 <= level5); + Assert.True(level7 <= level6); + Assert.True(level8 <= level7); + Assert.True(level9 <= level8); } } } diff --git a/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.ZLib.cs b/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.ZLib.cs index a7da0d162414a2..bff4dd70b60a7f 100644 --- a/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.ZLib.cs +++ b/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.ZLib.cs @@ -19,6 +19,7 @@ public class ZLibStreamUnitTests : CompressionStreamUnitTestBase public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new ZLibStream(stream, mode, leaveOpen); public override Stream CreateStream(Stream stream, CompressionLevel level) => new ZLibStream(stream, level); public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new ZLibStream(stream, level, leaveOpen); + public override Stream CreateStream(Stream stream, ZLibCompressionOptions options, bool leaveOpen) => new ZLibStream(stream, options, leaveOpen); public override Stream BaseStream(Stream stream) => ((ZLibStream)stream).BaseStream; protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("ZLibTestData", Path.GetFileName(uncompressedPath) + ".z"); @@ -153,60 +154,8 @@ public void StreamTruncation_IsDetected(TestScenario testScenario) } [Theory] - [MemberData(nameof(ZLibOptionsRoundTripTestData))] - public async Task RoundTripWithOptions(string testFile, ZLibCompressionOptions options) - { - var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); - var compressedStream = CompressTestFile(uncompressedStream, options); - using var decompressor = new ZLibStream(compressedStream, mode: CompressionMode.Decompress); - var decompressorOutput = new MemoryStream(); - int _bufferSize = 1024; - var bytes = new byte[_bufferSize]; - bool finished = false; - int retCount; - while (!finished) - { - retCount = await decompressor.ReadAsync(bytes, 0, _bufferSize); - - if (retCount != 0) - await decompressorOutput.WriteAsync(bytes, 0, retCount); - else - finished = true; - } - decompressor.Dispose(); - decompressorOutput.Position = 0; - uncompressedStream.Position = 0; - - byte[] uncompressedStreamBytes = uncompressedStream.ToArray(); - byte[] decompressorOutputBytes = decompressorOutput.ToArray(); - - Assert.Equal(uncompressedStreamBytes.Length, decompressorOutputBytes.Length); - for (int i = 0; i < uncompressedStreamBytes.Length; i++) - { - Assert.Equal(uncompressedStreamBytes[i], decompressorOutputBytes[i]); - } - } - - private MemoryStream CompressTestFile(LocalMemoryStream testStream, ZLibCompressionOptions options) - { - var compressorOutput = new MemoryStream(); - using (var compressionStream = new ZLibStream(compressorOutput, options, leaveOpen: true)) - { - var buffer = new byte[4096]; - int bytesRead; - while ((bytesRead = testStream.Read(buffer, 0, buffer.Length)) > 0) - { - compressionStream.Write(buffer, 0, bytesRead); - } - } - - compressorOutput.Position = 0; - return compressorOutput; - } - - [Theory] - [MemberData(nameof(UncompressedTestFiles))] - public async void ZlibCompression_SizeInOrder(string testFile) + [MemberData(nameof(UncompressedTestFilesZLib))] + public async void ZLibCompressionLevel_SizeInOrder(string testFile) { using var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); @@ -214,7 +163,7 @@ async Task GetLengthAsync(int compressionLevel) { uncompressedStream.Position = 0; using var mms = new MemoryStream(); - using var compressor = new ZLibStream(mms, new ZLibCompressionOptions() { CompressionLevel = compressionLevel, CompressionStrategy = ZLibCompressionStrategy.Fixed }); + using var compressor = new ZLibStream(mms, new ZLibCompressionOptions() { CompressionLevel = compressionLevel, CompressionStrategy = ZLibCompressionStrategy.HuffmanOnly }, leaveOpen: false); await uncompressedStream.CopyToAsync(compressor); await compressor.FlushAsync(); return mms.Length; @@ -231,15 +180,15 @@ async Task GetLengthAsync(int compressionLevel) long level8 = await GetLengthAsync(8); long level9 = await GetLengthAsync(9); - // Depending on the file type the compression level is not linearly affect the compressed size Assert.True(level1 <= level0); Assert.True(level2 <= level1); Assert.True(level3 <= level2); - Assert.True(level4 <= level2); - Assert.True(level5 <= level3); - Assert.True(level6 <= level3); - Assert.True(level8 <= level5); - Assert.True(level9 <= level5); + Assert.True(level4 <= level3); + Assert.True(level5 <= level4); + Assert.True(level6 <= level5); + Assert.True(level7 <= level6); + Assert.True(level8 <= level7); + Assert.True(level9 <= level8); } } }