-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
There is a desire to expose compression-algorithm specific configuration when constructing compression streams. The same options can be used for ZLibStream
, DeflateStream
, GZipStream
. Named those options with Zlib
prefix same as the native library name they use another prefix we considered is Deflate
- the algorithm name, the prefix needed because we have existing CompressionLevel
enum that used for all compression streams including BrotliStream
and it cannot be used for fine tuning the compression level for specific stream
public enum CompressionLevel | |
{ | |
Optimal = 0, | |
Fastest = 1, | |
NoCompression = 2, | |
SmallestSize = 3, | |
} |
API Proposal
namespace System.IO.Compression
{
public enum ZlibCompressionLevel
{
DefaultCompression = -1,
NoCompression = 0,
Level1 = 1,
BestSpeed = Level1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
Level5 = 5,
Level6 = 6,
Level7 = 7,
Level8 = 8,
Level9 = 9,
BestCompression = Level9,
}
public enum ZlibCompressionStrategy
{
DefaultStrategy = 0,
Filtered = 1,
HuffmanOnly = 2,
Rle = 3,
Fixed = 4
}
public enum ZlibFlushMode
{
NoFlush = 0,
PartialFlush = 1,
SyncFlush = 2,
FullFlush = 3,
Finish = 4,
}
public sealed class ZLibStream : Stream
{
// CompressionMode.Compress
public ZLibStream(Stream stream, ZlibCompressionLevel compressionLevel = ZlibCompressionLevel.DefaultCompression,
ZlibCompressionStrategy strategy = ZlibCompressionStrategy.DefaultStrategy, bool leaveOpen = false);
public ZlibFlushMode FlushMode { get; set; }
}
public partial class DeflateStream : Stream
{
public DeflateStream(Stream stream, ZlibCompressionLevel compressionLevel = ZlibCompressionLevel.DefaultCompression,
ZlibCompressionStrategy strategy = ZlibCompressionStrategy.DefaultStrategy, bool leaveOpen = false);
public ZlibFlushMode FlushMode { get; set; }
}
public partial class GZipStream : Stream
{
public GZipStream(Stream stream, ZlibCompressionLevel compressionLevel = ZlibCompressionLevel.DefaultCompression,
ZlibCompressionStrategy strategy = ZlibCompressionStrategy.DefaultStrategy, bool leaveOpen = false) { }
public ZlibFlushMode FlushMode { get; set; }
}
public enum BrotliCompressionQuality
{
NoCompression,
Quality1,
Quality2,
Quality3,
Quality4,
Quality5,
Quality6,
Quality7,
Quality8,
Quality9,
Quality10,
Quality11
}
public sealed partial class BrotliStream : System.IO.Stream
{
public BrotliStream(Stream stream, BrotliCompressionQuality quality = BrotliCompressionQuality.Quality4, bool leaveOpen = false) { }
}
}
ZlibCompressionLevel
, ZlibCompressionStrategy
are only for Compress mode.
Currently ZlibFlushMode.NoFlush
used in normal Read/Write
operations, ZlibFlushMode.SyncFlush
used for Stream.Flush and ZlibFlushMode.Finish
is used on dispose. The value set by the new FlushMode
property will be used for normal Read/Write
operations only.
The MemoryLevel
and/or WindowBits
options are omitted because there is no ask for them, we could add these and other options if/when they are requested.
API Usage
private MemoryStream CompressStream(Stream uncompressedStream)
{
var compressorOutput = new MemoryStream();
using (var compressionStream = new ZLibStream(compressorOutput, compressionLevel: ZlibCompressionLevel.Level5, strategy: ZlibCompressionStrategy.Filtered, leaveOpen: true))
{
compressionStream.FlushMode = ZlibFlushMode.NoFlush;
var buffer = new byte[4096];
int bytesRead;
while ((bytesRead = uncompressedStream.Read(buffer, 0, buffer.Length)) > 0)
{
compressionStream.Write(buffer, 0, bytesRead);
}
}
compressorOutput.Position = 0;
return compressorOutput;
}
Alternative Designs
An alternative design would introduce an options type, maybe per each stream, like so:
namespace System.IO.Compression
{
public class BrotliOptions
{
public readonly BrotliCompressionQualityQuality { get; }
public readonly int WindowBits { get; }
public BrotliOptions(BrotliCompressionQuality quality = BrotliCompressionQuality.Level4, int windowBits = 22) { }
}
public sealed class BrotliStream : Stream
{
public BrotliStream(Stream stream, BrotliOptions options, bool leaveOpen = false);
}
public struct ZLibOptions
{
public int WindowBits { get; }
public ZlibMemoryLevel MemoryLevel { get; }
public int CompressionLevel { get; }
public int CompressionStrategy { get; }
public ZlibOptions(ZlibCompressionLevel compressionLevel = ZlibCompressionLevel.DefaultCompression,
ZlibCompressionStrategy strategy = ZlibCompressionStrategy.DefaultStrategy,
ZlibMemoryLevel memoryLevel = ZlibMemoryLevel.Level8, int windowBits = -1);
}
public sealed class ZLibStream : Stream
{
public ZLibStream(Stream stream, ZLibOptions options, bool leaveOpen = false);
}
public class DeflateStream : Stream
{
public DeflateStream(Stream stream, ZLibOptions options, bool leaveOpen = false);
}
public class GZipStream : Stream
{
public GZipStream(Stream stream, ZLibOptionsoptions, bool leaveOpen = false);
}
}
We can then decide whether we want to be in the business of defining enums for the underlying types or whether we consider them passthru.