Skip to content

Add support for zlib data format (RFC 1950) #2236

@Ryder25

Description

@Ryder25

Currently .Net doesn't support the zlib data format despite the majority of the work having been done by DeflateStream. I propose a new class be added similar to GZipStream to support RFC 1950.

Personally I'm encountering the zlib format in two areas. Many games depend on zlib, and save files using this format. We also use zlib in the firmware of one of our hardware products. We save debug data and compress it with zlib. For both of these scenarios, we use C# tooling, on the desktop, to operate on these files whether it is for the purpose of reading, modifying, or writing.

Due to the lack of support in .Net I either have to use lame hacky methods, or rely on a separate third party library for the functionality. This is such a shame when .Net already provides the majority of what's needed, and it should be trivial (as far I can tell) to include this as part of .Net.

While decompression is quite simple to do by skipping the 2-6 byte header, and 4 byte CRC at the end, I have trouble with compression. I wouldn't know what the proper header should be when using DeflateStream to do the compression, not to mention having to include a computation for the Adler32 checksum.

Here's a quick untested example of what I currently have to do for decompression to give you an idea of the "lame hacky-ness": (Note: I'm not checking the checksum here. I'm assuming the data is good.)

private byte[] ZlibDecompress(byte[] data)
{
    using (var ms = new MemoryStream())
    using (var ds = new DeflateStream(ms, CompressionMode.Decompress))
    using (var msOut = new MemoryStream())
    {
        int headerSize = (data[1] & 0b10000) == 0b10000 ? 6 : 2;
        ms.Write(data, headerSize, data.Length - 4 - headerSize);
        ms.Seek(0, SeekOrigin.Begin);
        ds.CopyTo(msOut);
        return msOut.ToArray();
     }
}

Proposed API

public class ZLibStream : Stream
{
    public ZLibStream(Stream stream, CompressionLevel compressionLevel);
    public ZLibStream(Stream stream, CompressionMode mode);
    public ZLibStream(Stream stream, CompressionLevel compressionLevel, bool leaveOpen);
    public ZLibStream(Stream stream, CompressionMode mode, bool leaveOpen);
    public override bool CanWrite { get; }
    public override bool CanSeek { get; }
    public override bool CanRead { get; }
    public Stream BaseStream { get; }
    public override long Length { get; }
    public override long Position { get; set; }
    public override void CopyTo(Stream destination, int bufferSize);
    public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken);
    public override ValueTask DisposeAsync();
    public override void Flush();
    public override Task FlushAsync(CancellationToken cancellationToken);
    public override int Read(Span<byte> buffer);
    public override int Read(byte[] array, int offset, int count);
    public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default);
    public override Task<int> ReadAsync(byte[] array, int offset, int count, CancellationToken cancellationToken);
    public override int ReadByte();
    public override long Seek(long offset, SeekOrigin origin);
    public override void SetLength(long value);
    public override void Write(byte[] array, int offset, int count);
    public override void Write(ReadOnlySpan<byte> buffer);
    public override Task WriteAsync(byte[] array, int offset, int count, CancellationToken cancellationToken);
    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default);
    protected override void Dispose(bool disposing);
}

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-System.IO.Compressionhelp wanted[up-for-grabs] Good issue for external contributors

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions