Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 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 @@ -285,6 +285,7 @@ public sealed partial class ZstandardDecoder : System.IDisposable
{
public ZstandardDecoder() { }
public ZstandardDecoder(int maxWindowLog) { }
public ZstandardDecoder(System.IO.Compression.ZstandardDecompressionOptions decompressionOptions) { }
public ZstandardDecoder(System.IO.Compression.ZstandardDictionary dictionary) { }
public ZstandardDecoder(System.IO.Compression.ZstandardDictionary dictionary, int maxWindowLog) { }
public System.Buffers.OperationStatus Decompress(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesConsumed, out int bytesWritten) { throw null; }
Expand All @@ -297,6 +298,14 @@ public void SetPrefix(System.ReadOnlyMemory<byte> prefix) { }
}
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("wasi")]
public sealed partial class ZstandardDecompressionOptions
{
public ZstandardDecompressionOptions() { }
public System.IO.Compression.ZstandardDictionary? Dictionary { get { throw null; } set { } }
public int MaxWindowLog { get { throw null; } set { } }
}
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("wasi")]
public sealed partial class ZstandardDictionary : System.IDisposable
{
internal ZstandardDictionary() { }
Expand Down Expand Up @@ -338,6 +347,7 @@ public ZstandardStream(System.IO.Stream stream, System.IO.Compression.Compressio
public ZstandardStream(System.IO.Stream stream, System.IO.Compression.CompressionMode mode, System.IO.Compression.ZstandardDictionary dictionary, bool leaveOpen = false) { }
public ZstandardStream(System.IO.Stream stream, System.IO.Compression.ZstandardCompressionOptions compressionOptions, bool leaveOpen = false) { }
public ZstandardStream(System.IO.Stream stream, System.IO.Compression.ZstandardDecoder decoder, bool leaveOpen = false) { }
public ZstandardStream(System.IO.Stream stream, System.IO.Compression.ZstandardDecompressionOptions decompressionOptions, bool leaveOpen = false) { }
public ZstandardStream(System.IO.Stream stream, System.IO.Compression.ZstandardEncoder encoder, bool leaveOpen = false) { }
public System.IO.Stream BaseStream { get { throw null; } }
public override bool CanRead { get { throw null; } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<Compile Include="$(CommonPath)Interop\Interop.Zstd.cs" />
<Compile Include="System\IO\Compression\Zstandard\ZstandardCompressionOptions.cs" />
<Compile Include="System\IO\Compression\Zstandard\ZstandardDecoder.cs" />
<Compile Include="System\IO\Compression\Zstandard\ZstandardDecompressionOptions.cs" />
<Compile Include="System\IO\Compression\Zstandard\ZstandardDictionary.cs" />
<Compile Include="System\IO\Compression\Zstandard\ZstandardEncoder.cs" />
<Compile Include="System\IO\Compression\Zstandard\ZstandardStream.Compress.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;

namespace System.IO.Compression
{
[System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatform("wasi")]
public sealed class ZstandardDecompressionOptions
{
public ZstandardDecompressionOptions() => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public ZstandardDictionary? Dictionary { get => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression); set => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression); }
public int MaxWindowLog { get => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression); set => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression); }
}

[System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatform("wasi")]
public sealed class ZstandardCompressionOptions
Expand All @@ -28,6 +39,7 @@ public sealed class ZstandardDecoder : IDisposable
{
public ZstandardDecoder() => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public ZstandardDecoder(int maxWindowLog) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public ZstandardDecoder(ZstandardDecompressionOptions decompressionOptions) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public ZstandardDecoder(ZstandardDictionary dictionary) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public ZstandardDecoder(ZstandardDictionary dictionary, int maxWindowLog) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public System.Buffers.OperationStatus Decompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesConsumed, out int bytesWritten) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
Expand Down Expand Up @@ -84,6 +96,7 @@ public sealed partial class ZstandardStream : Stream
public ZstandardStream(Stream stream, CompressionMode mode, ZstandardDictionary dictionary, bool leaveOpen = false) : base() => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public ZstandardStream(Stream stream, ZstandardCompressionOptions compressionOptions, bool leaveOpen = false) : base() => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public ZstandardStream(Stream stream, ZstandardDecoder decoder, bool leaveOpen = false) : base() => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public ZstandardStream(Stream stream, ZstandardDecompressionOptions decompressionOptions, bool leaveOpen = false) : base() => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public ZstandardStream(Stream stream, ZstandardEncoder encoder, bool leaveOpen = false) : base() => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public Stream BaseStream => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
public override bool CanRead => throw new PlatformNotSupportedException(SR.PlatformNotSupported_ZstandardCompression);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Buffers;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

Expand Down Expand Up @@ -78,6 +79,37 @@ public ZstandardDecoder(ZstandardDictionary dictionary)
}
}

/// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified decompression options.</summary>
/// <param name="decompressionOptions">The options to use for Zstandard decompression.</param>
/// <exception cref="ArgumentNullException"><paramref name="decompressionOptions"/> is null.</exception>
/// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
public ZstandardDecoder(ZstandardDecompressionOptions decompressionOptions)
{
ArgumentNullException.ThrowIfNull(decompressionOptions);

_disposed = false;

InitializeDecoder();

try
{
if (decompressionOptions.MaxWindowLog != 0)
{
SetWindowLog(decompressionOptions.MaxWindowLog);
}

if (decompressionOptions.Dictionary is not null)
{
SetDictionary(decompressionOptions.Dictionary);
}
}
catch
{
_context.Dispose();
throw;
}
}

/// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified dictionary and maximum window size.</summary>
/// <param name="dictionary">The decompression dictionary to use.</param>
/// <param name="maxWindowLog">The maximum window size to use for decompression, expressed as base 2 logarithm.</param>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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>Provides decompression options to be used with Zstandard decompression.</summary>
[System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatform("wasi")]
public sealed class ZstandardDecompressionOptions
{

/// <summary>Gets or sets the maximum allowed window size when decompressing payloads, expressed as base 2 logarithm.</summary>
/// <value>The maximum window size for decompression, expressed as base 2 logarithm.</value>
/// <remarks>
/// The valid range is from <see cref="ZstandardCompressionOptions.MinWindowLog"/> to <see cref="ZstandardCompressionOptions.MaxWindowLog"/>.
/// Value 0 indicates the implementation-defined default window size.
/// </remarks>
/// <exception cref="ArgumentOutOfRangeException">The value is not 0 and is not between <see cref="ZstandardCompressionOptions.MinWindowLog"/> and <see cref="ZstandardCompressionOptions.MaxWindowLog"/>.</exception>
public int MaxWindowLog
{
get;
set
{
if (value != 0)
{
ArgumentOutOfRangeException.ThrowIfLessThan(value, ZstandardUtils.WindowLog_Min, nameof(value));
ArgumentOutOfRangeException.ThrowIfGreaterThan(value, ZstandardUtils.WindowLog_Max, nameof(value));
}

field = value;
}
}

/// <summary>Gets or sets the dictionary to use for decompression.</summary>
/// <value>The decompression dictionary, or <see langword="null"/> if no dictionary is used.</value>
public ZstandardDictionary? Dictionary { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ public ZstandardStream(Stream stream, ZstandardDecoder decoder, bool leaveOpen =
_encoderOwned = false;
}

/// <summary>Initializes a new instance of the <see cref="ZstandardStream" /> class by using the specified stream, decompression options, and optionally leaves the stream open.</summary>
/// <param name="stream">The stream from which data to decompress is read.</param>
/// <param name="decompressionOptions">The options to use for Zstandard decompression.</param>
/// <param name="leaveOpen"><see langword="true" /> to leave the stream open after the <see cref="ZstandardStream" /> object is disposed; otherwise, <see langword="false" />.</param>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> or <paramref name="decompressionOptions"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="stream"/> does not support reading.</exception>
public ZstandardStream(Stream stream, ZstandardDecompressionOptions decompressionOptions, bool leaveOpen = false)
{
ArgumentNullException.ThrowIfNull(decompressionOptions);

Init(stream, CompressionMode.Decompress);
_mode = CompressionMode.Decompress;
_leaveOpen = leaveOpen;

_decoder = new ZstandardDecoder(decompressionOptions);
}

private bool TryDecompress(Span<byte> destination, out int bytesWritten, out OperationStatus lastResult)
{
Debug.Assert(_decoder != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,5 +282,82 @@ public void StreamTruncation_IsDetected(TestScenario testScenario)
}, testScenario.ToString()).Dispose();
}

[Fact]
public void ZstandardStream_WithDecompressionOptions_DecompressesData()
{
byte[] testData = ZstandardTestUtils.CreateTestData();
byte[] compressedData = new byte[ZstandardEncoder.GetMaxCompressedLength(testData.Length)];
Assert.True(ZstandardEncoder.TryCompress(testData, compressedData, out int compressedLength));
Array.Resize(ref compressedData, compressedLength);

ZstandardDecompressionOptions options = new();
using MemoryStream input = new(compressedData);
using MemoryStream output = new();

using (ZstandardStream decompressionStream = new(input, options, leaveOpen: true))
{
decompressionStream.CopyTo(output);
}

Assert.Equal(testData, output.ToArray());
}
Comment thread
rzikm marked this conversation as resolved.

[Fact]
public void ZstandardStream_WithDecompressionOptions_NullOptions_ThrowsArgumentNullException()
{
using MemoryStream input = new();
Assert.Throws<ArgumentNullException>("decompressionOptions", () => new ZstandardStream(input, (ZstandardDecompressionOptions)null!));
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task ZstandardStream_WithDecompressionOptions_WithDictionary_RoundTrips(bool async)
{
byte[] dictionaryData = ZstandardTestUtils.CreateSampleDictionary();
using ZstandardDictionary dictionary = ZstandardDictionary.Create(dictionaryData);

byte[] testData = ZstandardTestUtils.CreateTestData(5000);

using MemoryStream compressedStream = new();
using (ZstandardStream compressionStream = new(compressedStream, CompressionMode.Compress, dictionary, leaveOpen: true))
{
if (async)
await compressionStream.WriteAsync(testData, 0, testData.Length);
else
compressionStream.Write(testData, 0, testData.Length);
}

compressedStream.Position = 0;

ZstandardDecompressionOptions options = new() { Dictionary = dictionary };
using MemoryStream decompressedStream = new();
using (ZstandardStream decompressionStream = new(compressedStream, options))
{
if (async)
await decompressionStream.CopyToAsync(decompressedStream);
else
decompressionStream.CopyTo(decompressedStream);
}

Assert.Equal(testData, decompressedStream.ToArray());
}

[Fact]
public void ZstandardStream_WithDecompressionOptions_DisposedStream_ThrowsObjectDisposedException()
{
byte[] testData = ZstandardTestUtils.CreateTestData();
byte[] compressedData = new byte[ZstandardEncoder.GetMaxCompressedLength(testData.Length)];
Assert.True(ZstandardEncoder.TryCompress(testData, compressedData, out int compressedLength));
Array.Resize(ref compressedData, compressedLength);

ZstandardDecompressionOptions options = new();
using MemoryStream input = new(compressedData);
ZstandardStream decompressionStream = new(input, options);
decompressionStream.Dispose();

Assert.Throws<ObjectDisposedException>(() => decompressionStream.Read(new byte[1], 0, 1));
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,45 @@ public void TargetBlockSize_SetOutOfRange_ThrowsArgumentOutOfRangeException(int
Assert.Throws<ArgumentOutOfRangeException>(() => options.TargetBlockSize = targetBlockSize);
}
}
}

public class ZstandardDecompressionOptionsTests
{
[Theory]
[InlineData(0)]
[InlineData(10)]
[InlineData(23)]
[InlineData(30)]
public void MaxWindowLog_SetToValidRange_Succeeds(int maxWindowLog)
{
ZstandardDecompressionOptions options = new();
options.MaxWindowLog = maxWindowLog;
Assert.Equal(maxWindowLog, options.MaxWindowLog);
}

[Theory]
[InlineData(1)]
[InlineData(9)]
[InlineData(32)]
public void MaxWindowLog_SetOutOfRange_ThrowsArgumentOutOfRangeException(int maxWindowLog)
Comment thread
rzikm marked this conversation as resolved.
{
ZstandardDecompressionOptions options = new();
Assert.Throws<ArgumentOutOfRangeException>(() => options.MaxWindowLog = maxWindowLog);
}

[Fact]
public void Dictionary_SetAndGet_RoundTrips()
{
using ZstandardDictionary dictionary = ZstandardDictionary.Create(ZstandardTestUtils.CreateSampleDictionary());
ZstandardDecompressionOptions options = new();
options.Dictionary = dictionary;
Assert.Same(dictionary, options.Dictionary);
}

[Fact]
public void Dictionary_DefaultValue_IsNull()
{
ZstandardDecompressionOptions options = new();
Assert.Null(options.Dictionary);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -576,5 +576,51 @@ public void Compress_AppendChecksum_RoundTrip(bool corrupt)
Assert.Equal(input, decompressed);
}
}

[Fact]
public void Decoder_Ctor_DecompressionOptions_Null_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>("decompressionOptions", () => new ZstandardDecoder((ZstandardDecompressionOptions)null!));
}

[Theory]
[InlineData(0)]
[InlineData(10)]
public void Decoder_Ctor_DecompressionOptions_Succeeds(int maxWindowLog)
{
ZstandardDecompressionOptions options = new() { MaxWindowLog = maxWindowLog };
using ZstandardDecoder decoder = new(options);

byte[] testData = ZstandardTestUtils.CreateTestData(100);
byte[] compressed = new byte[ZstandardEncoder.GetMaxCompressedLength(testData.Length)];
Assert.True(ZstandardEncoder.TryCompress(testData, compressed, out int compressedLength));

byte[] decompressed = new byte[testData.Length];
OperationStatus status = decoder.Decompress(compressed.AsSpan(0, compressedLength), decompressed, out _, out int bytesWritten);
Assert.Equal(OperationStatus.Done, status);
Assert.Equal(testData, decompressed.AsSpan(0, bytesWritten).ToArray());
}

[Fact]
public void Decoder_Ctor_DecompressionOptions_WithDictionary_RoundTrips()
{
byte[] dictionaryData = ZstandardTestUtils.CreateSampleDictionary();
using ZstandardDictionary dictionary = ZstandardDictionary.Create(dictionaryData);

byte[] testData = ZstandardTestUtils.CreateTestData(500);

// Compress with dictionary
using ZstandardEncoder encoder = new(dictionary);
byte[] compressed = new byte[ZstandardEncoder.GetMaxCompressedLength(testData.Length)];
Assert.Equal(OperationStatus.Done, encoder.Compress(testData, compressed, out _, out int compressedLength, isFinalBlock: true));

// Decompress with ZstandardDecompressionOptions containing the dictionary
ZstandardDecompressionOptions options = new() { Dictionary = dictionary };
using ZstandardDecoder decoder = new(options);
byte[] decompressed = new byte[testData.Length];
OperationStatus status = decoder.Decompress(compressed.AsSpan(0, compressedLength), decompressed, out _, out int bytesWritten);
Assert.Equal(OperationStatus.Done, status);
Assert.Equal(testData, decompressed.AsSpan(0, bytesWritten).ToArray());
}
}
}
Loading