Skip to content

Add XZ (LZMA) compression support to System.IO.Compression #1542

@joshfree

Description

@joshfree

Background and motivation

The goal of this proposal is to expose LZMA/XZ compression and decompression through APIs that follow the established patterns in System.IO.Compression.
The native library will be shipped with #124003

API Proposal

namespace System.IO.Compression
{
  public enum XzChecksumType
  {
      None = 0,
      Crc32 = 1,
      Crc64 = 2,
      Sha256 = 3,
  }
  public sealed partial class XzCompressionOptions
  {
      public XzCompressionOptions() { }
      public static int DefaultQuality { get { throw null; } }
      public static int DefaultWindowLog { get { throw null; } }
      public static int MaxQuality { get { throw null; } }
      public static int MaxWindowLog { get { throw null; } }
      public static int MinQuality { get { throw null; } }
      public static int MinWindowLog { get { throw null; } }
      public System.IO.Compression.XzChecksumType Checksum { get { throw null; } set { } }
      public int WindowLog { get { throw null; } set { } }
      public bool EnableExtremeMode { get { throw null; } set { } }
      public int Quality { get { throw null; } set { } }
  }
  public sealed partial class XzDecoder : System.IDisposable
  {
      public XzDecoder() { }
      public XzDecoder(int maxWindowLog) { }
      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 XzEncoder : System.IDisposable
  {
      public XzEncoder() { }
      public XzEncoder(int quality) { }
      public XzEncoder(int quality, int windowLog) { }
      public XzEncoder(System.IO.Compression.XzCompressionOptions compressionOptions) { }
      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 XzStream : System.IO.Stream
  {
      public XzStream(System.IO.Stream stream, System.IO.Compression.CompressionLevel compressionLevel, bool leaveOpen = false) { }
      public XzStream(System.IO.Stream stream, System.IO.Compression.CompressionMode mode, bool leaveOpen = false) { }
      public XzStream(System.IO.Stream stream, System.IO.Compression.XzCompressionOptions compressionOptions, bool leaveOpen = false) { }
      public System.IO.Stream BaseStream { get { throw null; } }
      public override bool CanRead { get { throw null; } }
      public override bool CanSeek { get { throw null; } }
      public override bool CanWrite { get { throw null; } }
      public override long Length { get { throw null; } }
      public override long Position { get { throw null; } set { } }
      public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; }
      public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; }
      protected override void Dispose(bool disposing) { }
      public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
      public override int EndRead(System.IAsyncResult asyncResult) { throw null; }
      public override void EndWrite(System.IAsyncResult asyncResult) { }
      public override void Flush() { }
      public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
      public override int Read(byte[] buffer, int offset, int count) { throw null; }
      public override int Read(System.Span<byte> buffer) { throw null; }
      public override System.Threading.Tasks.Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; }
      public override System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<byte> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
      public override int ReadByte() { throw null; }
      public override long Seek(long offset, System.IO.SeekOrigin origin) { throw null; }
      public override void SetLength(long value) { }
      public override void Write(byte[] buffer, int offset, int count) { }
      public override void Write(System.ReadOnlySpan<byte> buffer) { }
      public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; }
      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) { }
  }
}  

Alternative Designs

  1. Use raw byte values for dictionary/window size instead of log₂ value
    LZMA natively uses raw byte values for dict_size (e.g., 8388608 for 8 MiB), while Zstandard uses log₂ values for windowLog . We chose log₂ to maintain consistency with ZstandardCompressionOptions.WindowLog.

  2. Don't expose EnableExtremeMode
    The EnableExtremeMode flag (maps to LZMA_PRESET_EXTREME)provides marginal compression improvement at significant speed cost, and no other compression API in System.IO.Compression exposes such an analogous toggle.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions