From 405dbb30cde5dfc488de4bb52d897f17a52c5495 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:50:54 +0000 Subject: [PATCH 1/8] Initial plan From cab3e7d498127f779d27e42ce1f1c18c994f222c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:55:37 +0000 Subject: [PATCH 2/8] Initial analysis: Planning compile flags consolidation Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/packages.lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 41325333..032c15c4 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -216,9 +216,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "ISahzLHsHY7vrwqr2p1YWZ+gsxoBRtH7gWRDK8fDUst9pp2He0GiesaqEfeX0V8QMCJM3eNEHGGpnIcPjFo2NQ==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", From a5300f33836a25ce3a0b735acfe39a50b9027b9c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:05:14 +0000 Subject: [PATCH 3/8] Replace NETFRAMEWORK and NETSTANDARD2_0 with LEGACY_DOTNET compile flag Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/Algorithms/Adler32.cs | 2 +- src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs | 2 +- src/SharpCompress/Common/EntryStream.cs | 4 ++-- src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs | 4 ++-- src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs | 2 +- src/SharpCompress/Compressors/BZip2/BZip2Stream.cs | 2 +- src/SharpCompress/Compressors/Deflate/DeflateStream.cs | 6 +++--- src/SharpCompress/Compressors/Deflate/GZipStream.cs | 4 ++-- src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs | 4 ++-- src/SharpCompress/Compressors/Deflate/ZlibStream.cs | 6 +++--- .../Compressors/Deflate64/Deflate64Stream.cs | 2 +- src/SharpCompress/Compressors/LZMA/LZipStream.cs | 2 +- src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs | 2 +- src/SharpCompress/Compressors/LZMA/LzmaStream.cs | 2 +- .../Compressors/ZStandard/CompressionStream.cs | 8 ++++---- .../Compressors/ZStandard/DecompressionStream.cs | 6 +++--- src/SharpCompress/Crypto/Crc32Stream.cs | 2 +- src/SharpCompress/IO/BufferedSubStream.cs | 2 +- src/SharpCompress/IO/ProgressReportingStream.cs | 6 +++--- src/SharpCompress/IO/ReadOnlySubStream.cs | 4 ++-- src/SharpCompress/IO/SharpCompressStream.cs | 2 +- src/SharpCompress/IO/SourceStream.cs | 2 +- src/SharpCompress/NotNullExtensions.cs | 2 +- src/SharpCompress/Polyfills/StringExtensions.cs | 2 +- src/SharpCompress/Readers/AbstractReader.cs | 4 ++-- src/SharpCompress/SharpCompress.csproj | 3 +++ 26 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/SharpCompress/Algorithms/Adler32.cs b/src/SharpCompress/Algorithms/Adler32.cs index f7ea0eb3..d7645327 100644 --- a/src/SharpCompress/Algorithms/Adler32.cs +++ b/src/SharpCompress/Algorithms/Adler32.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -#if !NETSTANDARD2_0 && !NETSTANDARD2_1 && !NETFRAMEWORK +#if !LEGACY_DOTNET #define SUPPORTS_RUNTIME_INTRINSICS #define SUPPORTS_HOTPATH #endif diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index e6b511d8..328d5e33 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -184,7 +184,7 @@ public partial class SevenZipArchive : AbstractArchive ReadAsync( Memory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Common/EntryStream.cs b/src/SharpCompress/Common/EntryStream.cs index 11d0e898..e4de4ca9 100644 --- a/src/SharpCompress/Common/EntryStream.cs +++ b/src/SharpCompress/Common/EntryStream.cs @@ -93,7 +93,7 @@ public class EntryStream : Stream, IStreamStack _stream.Dispose(); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask DisposeAsync() { if (_isDisposed) @@ -171,7 +171,7 @@ public class EntryStream : Stream, IStreamStack return read; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs index 4e6ddb70..9a91ba9c 100644 --- a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs +++ b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs @@ -66,7 +66,7 @@ internal class TarReadOnlySubStream : SharpCompressStream, IStreamStack base.Dispose(disposing); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async System.Threading.Tasks.ValueTask DisposeAsync() { if (_isDisposed) @@ -170,7 +170,7 @@ internal class TarReadOnlySubStream : SharpCompressStream, IStreamStack return read; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async System.Threading.Tasks.ValueTask ReadAsync( System.Memory buffer, System.Threading.CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs index da37501b..9925c593 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs @@ -20,7 +20,7 @@ internal class WinzipAesEncryptionData { _keySize = keySize; -#if NETFRAMEWORK || NETSTANDARD2_0 +#if LEGACY_DOTNET var rfc2898 = new Rfc2898DeriveBytes(password, salt, RFC2898_ITERATIONS); KeyBytes = rfc2898.GetBytes(KeySizeInBytes); IvBytes = rfc2898.GetBytes(KeySizeInBytes); diff --git a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs index e3325f27..898839bc 100644 --- a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs +++ b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs @@ -98,7 +98,7 @@ public sealed class BZip2Stream : Stream, IStreamStack public override void SetLength(long value) => stream.SetLength(value); -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override int Read(Span buffer) => stream.Read(buffer); diff --git a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs index 55c24813..f11ea41b 100644 --- a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs +++ b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs @@ -299,7 +299,7 @@ public class DeflateStream : Stream, IStreamStack await _baseStream.FlushAsync(cancellationToken).ConfigureAwait(false); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask DisposeAsync() { if (_disposed) @@ -370,7 +370,7 @@ public class DeflateStream : Stream, IStreamStack .ConfigureAwait(false); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default @@ -461,7 +461,7 @@ public class DeflateStream : Stream, IStreamStack .ConfigureAwait(false); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask WriteAsync( ReadOnlyMemory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Compressors/Deflate/GZipStream.cs b/src/SharpCompress/Compressors/Deflate/GZipStream.cs index d9af4284..aceacb28 100644 --- a/src/SharpCompress/Compressors/Deflate/GZipStream.cs +++ b/src/SharpCompress/Compressors/Deflate/GZipStream.cs @@ -345,7 +345,7 @@ public class GZipStream : Stream, IStreamStack return n; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default @@ -454,7 +454,7 @@ public class GZipStream : Stream, IStreamStack await BaseStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask WriteAsync( ReadOnlyMemory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs index d3c10f9b..dd6590b7 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs @@ -552,7 +552,7 @@ internal class ZlibBaseStream : Stream, IStreamStack } } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask DisposeAsync() { if (isDisposed) @@ -1171,7 +1171,7 @@ internal class ZlibBaseStream : Stream, IStreamStack return rc; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs index 3365832c..d94ca210 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs @@ -277,7 +277,7 @@ public class ZlibStream : Stream, IStreamStack await _baseStream.FlushAsync(cancellationToken).ConfigureAwait(false); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask DisposeAsync() { if (_disposed) @@ -347,7 +347,7 @@ public class ZlibStream : Stream, IStreamStack .ConfigureAwait(false); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default @@ -431,7 +431,7 @@ public class ZlibStream : Stream, IStreamStack .ConfigureAwait(false); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask WriteAsync( ReadOnlyMemory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs index 3ba9e618..083f0380 100644 --- a/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs +++ b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs @@ -200,7 +200,7 @@ public sealed class Deflate64Stream : Stream, IStreamStack return count - remainingCount; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.cs index e409e39e..ddf39e01 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.cs @@ -163,7 +163,7 @@ public sealed class LZipStream : Stream, IStreamStack public override void SetLength(long value) => throw new NotImplementedException(); -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override ValueTask ReadAsync( Memory buffer, diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index 95d3d027..0fb39626 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -201,7 +201,7 @@ public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream } } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET [MemberNotNull(nameof(_outWindow))] #endif private void CreateDictionary() diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs index 26079966..1399c0bf 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs @@ -632,7 +632,7 @@ public class LzmaStream : Stream, IStreamStack return total; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Compressors/ZStandard/CompressionStream.cs b/src/SharpCompress/Compressors/ZStandard/CompressionStream.cs index af8865b4..df4b4d68 100644 --- a/src/SharpCompress/Compressors/ZStandard/CompressionStream.cs +++ b/src/SharpCompress/Compressors/ZStandard/CompressionStream.cs @@ -74,7 +74,7 @@ public class CompressionStream : Stream ~CompressionStream() => Dispose(false); -#if !NETSTANDARD2_0 && !NETFRAMEWORK +#if !LEGACY_DOTNET public override async ValueTask DisposeAsync() #else public async ValueTask DisposeAsync() @@ -145,7 +145,7 @@ public class CompressionStream : Stream public override void Write(byte[] buffer, int offset, int count) => Write(new ReadOnlySpan(buffer, offset, count)); -#if !NETSTANDARD2_0 && !NETFRAMEWORK +#if !LEGACY_DOTNET public override void Write(ReadOnlySpan buffer) => WriteInternal(buffer, ZSTD_EndDirective.ZSTD_e_continue); #else @@ -176,7 +176,7 @@ public class CompressionStream : Stream ); } -#if !NETSTANDARD2_0 && !NETFRAMEWORK +#if !LEGACY_DOTNET private async ValueTask WriteInternalAsync( ReadOnlyMemory? buffer, ZSTD_EndDirective directive, @@ -218,7 +218,7 @@ public class CompressionStream : Stream ); } -#if !NETSTANDARD2_0 && !NETFRAMEWORK +#if !LEGACY_DOTNET public override Task WriteAsync( byte[] buffer, diff --git a/src/SharpCompress/Compressors/ZStandard/DecompressionStream.cs b/src/SharpCompress/Compressors/ZStandard/DecompressionStream.cs index 78af4351..c1aed766 100644 --- a/src/SharpCompress/Compressors/ZStandard/DecompressionStream.cs +++ b/src/SharpCompress/Compressors/ZStandard/DecompressionStream.cs @@ -105,7 +105,7 @@ public class DecompressionStream : Stream public override int Read(byte[] buffer, int offset, int count) => Read(new Span(buffer, offset, count)); -#if !NETSTANDARD2_0 && !NETFRAMEWORK +#if !LEGACY_DOTNET public override int Read(Span buffer) #else public int Read(Span buffer) @@ -158,7 +158,7 @@ public class DecompressionStream : Stream } } -#if !NETSTANDARD2_0 && !NETFRAMEWORK +#if !LEGACY_DOTNET public override Task ReadAsync( byte[] buffer, int offset, @@ -276,7 +276,7 @@ public class DecompressionStream : Stream throw new ObjectDisposedException(nameof(DecompressionStream)); } -#if NETSTANDARD2_0 || NETFRAMEWORK +#if LEGACY_DOTNET public virtual Task DisposeAsync() { try diff --git a/src/SharpCompress/Crypto/Crc32Stream.cs b/src/SharpCompress/Crypto/Crc32Stream.cs index dfbcfac8..0dbcce8c 100644 --- a/src/SharpCompress/Crypto/Crc32Stream.cs +++ b/src/SharpCompress/Crypto/Crc32Stream.cs @@ -70,7 +70,7 @@ public sealed class Crc32Stream : Stream, IStreamStack public override void SetLength(long value) => throw new NotSupportedException(); -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override void Write(ReadOnlySpan buffer) { diff --git a/src/SharpCompress/IO/BufferedSubStream.cs b/src/SharpCompress/IO/BufferedSubStream.cs index 747c9776..7a51bb0f 100755 --- a/src/SharpCompress/IO/BufferedSubStream.cs +++ b/src/SharpCompress/IO/BufferedSubStream.cs @@ -154,7 +154,7 @@ internal class BufferedSubStream : SharpCompressStream, IStreamStack return count; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/IO/ProgressReportingStream.cs b/src/SharpCompress/IO/ProgressReportingStream.cs index 5ed02835..efadba41 100644 --- a/src/SharpCompress/IO/ProgressReportingStream.cs +++ b/src/SharpCompress/IO/ProgressReportingStream.cs @@ -64,7 +64,7 @@ internal sealed class ProgressReportingStream : Stream return bytesRead; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override int Read(Span buffer) { var bytesRead = _baseStream.Read(buffer); @@ -95,7 +95,7 @@ internal sealed class ProgressReportingStream : Stream return bytesRead; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default @@ -147,7 +147,7 @@ internal sealed class ProgressReportingStream : Stream base.Dispose(disposing); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask DisposeAsync() { if (!_leaveOpen) diff --git a/src/SharpCompress/IO/ReadOnlySubStream.cs b/src/SharpCompress/IO/ReadOnlySubStream.cs index 86ef1645..c9d7d830 100644 --- a/src/SharpCompress/IO/ReadOnlySubStream.cs +++ b/src/SharpCompress/IO/ReadOnlySubStream.cs @@ -81,7 +81,7 @@ internal class ReadOnlySubStream : SharpCompressStream, IStreamStack return value; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override int Read(Span buffer) { var sliceLen = BytesLeftToRead < buffer.Length ? BytesLeftToRead : buffer.Length; @@ -117,7 +117,7 @@ internal class ReadOnlySubStream : SharpCompressStream, IStreamStack return read; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default diff --git a/src/SharpCompress/IO/SharpCompressStream.cs b/src/SharpCompress/IO/SharpCompressStream.cs index 948fdb57..ad4c6e35 100644 --- a/src/SharpCompress/IO/SharpCompressStream.cs +++ b/src/SharpCompress/IO/SharpCompressStream.cs @@ -384,7 +384,7 @@ public class SharpCompressStream : Stream, IStreamStack await Stream.FlushAsync(cancellationToken).ConfigureAwait(false); } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, diff --git a/src/SharpCompress/IO/SourceStream.cs b/src/SharpCompress/IO/SourceStream.cs index 0712d915..e9cc74a6 100644 --- a/src/SharpCompress/IO/SourceStream.cs +++ b/src/SharpCompress/IO/SourceStream.cs @@ -289,7 +289,7 @@ public class SourceStream : Stream, IStreamStack return total - count; } -#if !NETFRAMEWORK && !NETSTANDARD2_0 +#if !LEGACY_DOTNET public override async ValueTask ReadAsync( Memory buffer, diff --git a/src/SharpCompress/NotNullExtensions.cs b/src/SharpCompress/NotNullExtensions.cs index 4fdd355c..0f5ba99c 100644 --- a/src/SharpCompress/NotNullExtensions.cs +++ b/src/SharpCompress/NotNullExtensions.cs @@ -21,7 +21,7 @@ internal static class NotNullExtensions return source.AsEnumerable(); } -#if NETFRAMEWORK || NETSTANDARD +#if LEGACY_DOTNET [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T NotNull(this T? obj, string? message = null) where T : class diff --git a/src/SharpCompress/Polyfills/StringExtensions.cs b/src/SharpCompress/Polyfills/StringExtensions.cs index 57d1e1bc..c83dee70 100644 --- a/src/SharpCompress/Polyfills/StringExtensions.cs +++ b/src/SharpCompress/Polyfills/StringExtensions.cs @@ -1,4 +1,4 @@ -#if NETFRAMEWORK || NETSTANDARD2_0 +#if LEGACY_DOTNET namespace SharpCompress; diff --git a/src/SharpCompress/Readers/AbstractReader.cs b/src/SharpCompress/Readers/AbstractReader.cs index 29e700aa..123576c2 100644 --- a/src/SharpCompress/Readers/AbstractReader.cs +++ b/src/SharpCompress/Readers/AbstractReader.cs @@ -277,7 +277,7 @@ public abstract class AbstractReader : IReader, IAsyncReader } } //don't know the size so we have to try to decompress to skip -#if NETFRAMEWORK || NETSTANDARD2_0 +#if LEGACY_DOTNET using var s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false); await s.SkipEntryAsync(cancellationToken).ConfigureAwait(false); #else @@ -344,7 +344,7 @@ public abstract class AbstractReader : IReader, IAsyncReader internal async ValueTask WriteAsync(Stream writeStream, CancellationToken cancellationToken) { -#if NETFRAMEWORK || NETSTANDARD2_0 +#if LEGACY_DOTNET using Stream s = await OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false); var sourceStream = WrapWithProgress(s, Entry); await sourceStream.CopyToAsync(writeStream, 81920, cancellationToken).ConfigureAwait(false); diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index a48b0b7a..2e9f4056 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -28,6 +28,9 @@ true $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + $(DefineConstants);LEGACY_DOTNET + true From 52fef492a5d7ab192c60075778f5b92e52dace5c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:09:06 +0000 Subject: [PATCH 4/8] Additional simplifications: Remove NETCF, fix NET60 typo, consolidate NETCOREAPP2_1 pattern Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/Compressors/Deflate/ZlibConstants.cs | 6 +----- .../Compressors/Rar/MultiVolumeReadOnlyStream.cs | 2 +- src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs | 2 +- src/SharpCompress/Compressors/Rar/RarCrcStream.cs | 2 +- src/SharpCompress/Compressors/Rar/RarStream.cs | 2 +- src/SharpCompress/Utility.cs | 2 +- 6 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/SharpCompress/Compressors/Deflate/ZlibConstants.cs b/src/SharpCompress/Compressors/Deflate/ZlibConstants.cs index ec415d9c..7bf8a3f6 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibConstants.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibConstants.cs @@ -108,13 +108,9 @@ internal static class ZlibConstants public const int Z_BUF_ERROR = -5; /// - /// The size of the working buffer used in the ZlibCodec class. Defaults to 8192 bytes. + /// The size of the working buffer used in the ZlibCodec class. Defaults to 16384 bytes. /// -#if NETCF - public const int WorkingBufferSizeDefault = 8192; -#else public const int WorkingBufferSizeDefault = 16384; -#endif /// /// The minimum size of the working buffer used in the ZlibCodec class. Currently it is 128 bytes. diff --git a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs index df1c5959..854b0885 100644 --- a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs @@ -185,7 +185,7 @@ internal sealed class MultiVolumeReadOnlyStream : Stream, IStreamStack return totalRead; } -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if !LEGACY_DOTNET public override async System.Threading.Tasks.ValueTask ReadAsync( Memory buffer, System.Threading.CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs b/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs index 693d284b..4ec3cf2c 100644 --- a/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarBLAKE2spStream.cs @@ -385,7 +385,7 @@ internal class RarBLAKE2spStream : RarStream, IStreamStack return result; } -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if !LEGACY_DOTNET public override async System.Threading.Tasks.ValueTask ReadAsync( Memory buffer, System.Threading.CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs index 0ec0527d..af866c17 100644 --- a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs @@ -129,7 +129,7 @@ internal class RarCrcStream : RarStream, IStreamStack return result; } -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if !LEGACY_DOTNET public override async System.Threading.Tasks.ValueTask ReadAsync( Memory buffer, System.Threading.CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Compressors/Rar/RarStream.cs b/src/SharpCompress/Compressors/Rar/RarStream.cs index 21c225c3..34700edf 100644 --- a/src/SharpCompress/Compressors/Rar/RarStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarStream.cs @@ -189,7 +189,7 @@ internal class RarStream : Stream, IStreamStack return outTotal; } -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if !LEGACY_DOTNET public override async System.Threading.Tasks.ValueTask ReadAsync( Memory buffer, System.Threading.CancellationToken cancellationToken = default diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 5fd18e6b..4863ac49 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -189,7 +189,7 @@ internal static class Utility } } -#if NET60_OR_GREATER +#if NET6_0_OR_GREATER public bool ReadFully(byte[] buffer) { try From d2cb792d91ca316080e8cfa97a0c843cf1ffa2b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:31:06 +0000 Subject: [PATCH 5/8] Change NET6_0_OR_GREATER to NET8_0_OR_GREATER Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/Common/AsyncBinaryReader.cs | 2 +- src/SharpCompress/Compressors/ZStandard/UnsafeHelper.cs | 6 +++--- src/SharpCompress/Utility.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SharpCompress/Common/AsyncBinaryReader.cs b/src/SharpCompress/Common/AsyncBinaryReader.cs index 8c5327ae..f2cdb4a6 100644 --- a/src/SharpCompress/Common/AsyncBinaryReader.cs +++ b/src/SharpCompress/Common/AsyncBinaryReader.cs @@ -82,7 +82,7 @@ namespace SharpCompress.Common } } -#if NET6_0_OR_GREATER +#if NET8_0_OR_GREATER public async ValueTask DisposeAsync() { if (_disposed) diff --git a/src/SharpCompress/Compressors/ZStandard/UnsafeHelper.cs b/src/SharpCompress/Compressors/ZStandard/UnsafeHelper.cs index b145099b..a73023c4 100644 --- a/src/SharpCompress/Compressors/ZStandard/UnsafeHelper.cs +++ b/src/SharpCompress/Compressors/ZStandard/UnsafeHelper.cs @@ -16,7 +16,7 @@ public static unsafe class UnsafeHelper [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void* malloc(ulong size) { -#if NET6_0_OR_GREATER +#if NET8_0_OR_GREATER var ptr = NativeMemory.Alloc((nuint)size); #else var ptr = (void*)Marshal.AllocHGlobal((nint)size); @@ -31,7 +31,7 @@ public static unsafe class UnsafeHelper [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void* calloc(ulong num, ulong size) { -#if NET6_0_OR_GREATER +#if NET8_0_OR_GREATER return NativeMemory.AllocZeroed((nuint)num, (nuint)size); #else var total = num * size; @@ -53,7 +53,7 @@ public static unsafe class UnsafeHelper [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void free(void* ptr) { -#if NET6_0_OR_GREATER +#if NET8_0_OR_GREATER NativeMemory.Free(ptr); #else Marshal.FreeHGlobal((IntPtr)ptr); diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 4863ac49..5b3ed2dd 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -189,7 +189,7 @@ internal static class Utility } } -#if NET6_0_OR_GREATER +#if NET8_0_OR_GREATER public bool ReadFully(byte[] buffer) { try From 4998676476a2657fee5b0debc585e82461c1287f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 07:22:01 +0000 Subject: [PATCH 6/8] Initial plan From 0e4a159998e34e08ae5644f969120a0c0e590f10 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 07:31:20 +0000 Subject: [PATCH 7/8] Add leaveOpen parameter to LZipStream and BZip2Stream Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .../Compressors/BZip2/BZip2Stream.cs | 18 ++++++++-- .../Compressors/BZip2/CBZip2InputStream.cs | 9 +++-- .../Compressors/BZip2/CBZip2OutputStream.cs | 13 ++++--- .../Compressors/LZMA/LZipStream.cs | 8 +++-- .../Compressors/LZMA/LzmaStream.cs | 36 ++++++++++++++----- .../Streams/DisposalTests.cs | 17 +++++++-- 6 files changed, 79 insertions(+), 22 deletions(-) diff --git a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs index 898839bc..03a9f6b8 100644 --- a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs +++ b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs @@ -30,6 +30,7 @@ public sealed class BZip2Stream : Stream, IStreamStack private readonly Stream stream; private bool isDisposed; + private readonly bool leaveOpen; /// /// Create a BZip2Stream @@ -37,19 +38,30 @@ public sealed class BZip2Stream : Stream, IStreamStack /// The stream to read from /// Compression Mode /// Decompress Concatenated - public BZip2Stream(Stream stream, CompressionMode compressionMode, bool decompressConcatenated) + /// Leave the stream open after disposing + public BZip2Stream( + Stream stream, + CompressionMode compressionMode, + bool decompressConcatenated, + bool leaveOpen = false + ) { #if DEBUG_STREAMS this.DebugConstruct(typeof(BZip2Stream)); #endif + this.leaveOpen = leaveOpen; Mode = compressionMode; if (Mode == CompressionMode.Compress) { - this.stream = new CBZip2OutputStream(stream); + this.stream = new CBZip2OutputStream(stream, 9, leaveOpen); } else { - this.stream = new CBZip2InputStream(stream, decompressConcatenated); + this.stream = new CBZip2InputStream( + stream, + decompressConcatenated, + leaveOpen: leaveOpen + ); } } diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs index e466cc07..dfbf1898 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs @@ -168,6 +168,7 @@ internal class CBZip2InputStream : Stream, IStreamStack private int computedBlockCRC, computedCombinedCRC; private readonly bool decompressConcatenated; + private readonly bool leaveOpen; private int i2, count, @@ -181,9 +182,10 @@ internal class CBZip2InputStream : Stream, IStreamStack private char z; private bool isDisposed; - public CBZip2InputStream(Stream zStream, bool decompressConcatenated) + public CBZip2InputStream(Stream zStream, bool decompressConcatenated, bool leaveOpen = false) { this.decompressConcatenated = decompressConcatenated; + this.leaveOpen = leaveOpen; ll8 = null; tt = null; BsSetStream(zStream); @@ -207,7 +209,10 @@ internal class CBZip2InputStream : Stream, IStreamStack this.DebugDispose(typeof(CBZip2InputStream)); #endif base.Dispose(disposing); - bsStream?.Dispose(); + if (!leaveOpen) + { + bsStream?.Dispose(); + } } internal static int[][] InitIntArray(int n1, int n2) diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs index 7ee38b81..e0500adc 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs @@ -341,12 +341,14 @@ internal sealed class CBZip2OutputStream : Stream, IStreamStack private int currentChar = -1; private int runLength; + private readonly bool leaveOpen; - public CBZip2OutputStream(Stream inStream) - : this(inStream, 9) { } + public CBZip2OutputStream(Stream inStream, bool leaveOpen = false) + : this(inStream, 9, leaveOpen) { } - public CBZip2OutputStream(Stream inStream, int inBlockSize) + public CBZip2OutputStream(Stream inStream, int inBlockSize, bool leaveOpen = false) { + this.leaveOpen = leaveOpen; block = null; quadrant = null; zptr = null; @@ -481,7 +483,10 @@ internal sealed class CBZip2OutputStream : Stream, IStreamStack this.DebugDispose(typeof(CBZip2OutputStream)); #endif Dispose(); - bsStream?.Dispose(); + if (!leaveOpen) + { + bsStream?.Dispose(); + } bsStream = null; } } diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.cs index ddf39e01..d03353f7 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.cs @@ -46,11 +46,13 @@ public sealed class LZipStream : Stream, IStreamStack private long _writeCount; private readonly Stream? _originalStream; + private readonly bool _leaveOpen; - public LZipStream(Stream stream, CompressionMode mode) + public LZipStream(Stream stream, CompressionMode mode, bool leaveOpen = false) { Mode = mode; _originalStream = stream; + _leaveOpen = leaveOpen; if (mode == CompressionMode.Decompress) { @@ -60,7 +62,7 @@ public sealed class LZipStream : Stream, IStreamStack throw new InvalidFormatException("Not an LZip stream"); } var properties = GetProperties(dSize); - _stream = new LzmaStream(properties, stream); + _stream = new LzmaStream(properties, stream, leaveOpen: leaveOpen); } else { @@ -127,7 +129,7 @@ public sealed class LZipStream : Stream, IStreamStack { Finish(); _stream.Dispose(); - if (Mode == CompressionMode.Compress) + if (Mode == CompressionMode.Compress && !_leaveOpen) { _originalStream?.Dispose(); } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs index 1399c0bf..2ddfdd8c 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs @@ -35,6 +35,7 @@ public class LzmaStream : Stream, IStreamStack private readonly Stream _inputStream; private readonly long _inputSize; private readonly long _outputSize; + private readonly bool _leaveOpen; private readonly int _dictionarySize; private readonly OutWindow _outWindow = new(); @@ -56,14 +57,28 @@ public class LzmaStream : Stream, IStreamStack private readonly Encoder _encoder; private bool _isDisposed; - public LzmaStream(byte[] properties, Stream inputStream) - : this(properties, inputStream, -1, -1, null, properties.Length < 5) { } + public LzmaStream(byte[] properties, Stream inputStream, bool leaveOpen = false) + : this(properties, inputStream, -1, -1, null, properties.Length < 5, leaveOpen) { } - public LzmaStream(byte[] properties, Stream inputStream, long inputSize) - : this(properties, inputStream, inputSize, -1, null, properties.Length < 5) { } + public LzmaStream(byte[] properties, Stream inputStream, long inputSize, bool leaveOpen = false) + : this(properties, inputStream, inputSize, -1, null, properties.Length < 5, leaveOpen) { } - public LzmaStream(byte[] properties, Stream inputStream, long inputSize, long outputSize) - : this(properties, inputStream, inputSize, outputSize, null, properties.Length < 5) { } + public LzmaStream( + byte[] properties, + Stream inputStream, + long inputSize, + long outputSize, + bool leaveOpen = false + ) + : this( + properties, + inputStream, + inputSize, + outputSize, + null, + properties.Length < 5, + leaveOpen + ) { } public LzmaStream( byte[] properties, @@ -71,13 +86,15 @@ public class LzmaStream : Stream, IStreamStack long inputSize, long outputSize, Stream presetDictionary, - bool isLzma2 + bool isLzma2, + bool leaveOpen = false ) { _inputStream = inputStream; _inputSize = inputSize; _outputSize = outputSize; _isLzma2 = isLzma2; + _leaveOpen = leaveOpen; #if DEBUG_STREAMS this.DebugConstruct(typeof(LzmaStream)); @@ -179,7 +196,10 @@ public class LzmaStream : Stream, IStreamStack { _position = _encoder.Code(null, true); } - _inputStream?.Dispose(); + if (!_leaveOpen) + { + _inputStream?.Dispose(); + } _outWindow.Dispose(); } base.Dispose(disposing); diff --git a/tests/SharpCompress.Test/Streams/DisposalTests.cs b/tests/SharpCompress.Test/Streams/DisposalTests.cs index 9d30ab76..08995892 100644 --- a/tests/SharpCompress.Test/Streams/DisposalTests.cs +++ b/tests/SharpCompress.Test/Streams/DisposalTests.cs @@ -2,6 +2,7 @@ using System; using System.IO; using SharpCompress.Common; using SharpCompress.Compressors; +using SharpCompress.Compressors.BZip2; using SharpCompress.Compressors.Deflate; using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.Lzw; @@ -152,9 +153,21 @@ public class DisposalTests [Fact] public void LZipStream_Disposal() { - // LZipStream always disposes inner stream + // LZipStream now supports leaveOpen parameter // Use Compress mode to avoid need for valid input header - VerifyAlwaysDispose(stream => new LZipStream(stream, CompressionMode.Compress)); + VerifyStreamDisposal( + (stream, leaveOpen) => new LZipStream(stream, CompressionMode.Compress, leaveOpen) + ); + } + + [Fact] + public void BZip2Stream_Disposal() + { + // BZip2Stream now supports leaveOpen parameter + VerifyStreamDisposal( + (stream, leaveOpen) => + new BZip2Stream(stream, CompressionMode.Compress, false, leaveOpen) + ); } [Fact] From 9454466be70c45b336ed5baf9b2299644e3d1f7e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 07:33:22 +0000 Subject: [PATCH 8/8] Add comprehensive tests for leaveOpen behavior and fix BZip2 stream disposal Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .../Compressors/BZip2/CBZip2InputStream.cs | 5 +- .../Streams/LeaveOpenBehaviorTests.cs | 226 ++++++++++++++++++ 2 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 tests/SharpCompress.Test/Streams/LeaveOpenBehaviorTests.cs diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs index dfbf1898..e3104408 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs @@ -403,7 +403,10 @@ internal class CBZip2InputStream : Stream, IStreamStack private void BsFinishedWithStream() { - bsStream?.Dispose(); + if (!leaveOpen) + { + bsStream?.Dispose(); + } bsStream = null; } diff --git a/tests/SharpCompress.Test/Streams/LeaveOpenBehaviorTests.cs b/tests/SharpCompress.Test/Streams/LeaveOpenBehaviorTests.cs new file mode 100644 index 00000000..76e0565e --- /dev/null +++ b/tests/SharpCompress.Test/Streams/LeaveOpenBehaviorTests.cs @@ -0,0 +1,226 @@ +using System; +using System.IO; +using System.Text; +using SharpCompress.Compressors; +using SharpCompress.Compressors.BZip2; +using SharpCompress.Compressors.LZMA; +using SharpCompress.Test.Mocks; +using Xunit; + +namespace SharpCompress.Test.Streams; + +public class LeaveOpenBehaviorTests +{ + private static byte[] CreateTestData() => + Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog"); + + [Fact] + public void BZip2Stream_Compress_LeaveOpen_False() + { + using var innerStream = new TestStream(new MemoryStream()); + using ( + var bzip2 = new BZip2Stream( + innerStream, + CompressionMode.Compress, + false, + leaveOpen: false + ) + ) + { + bzip2.Write(CreateTestData(), 0, CreateTestData().Length); + bzip2.Finish(); + } + + Assert.True(innerStream.IsDisposed, "Inner stream should be disposed when leaveOpen=false"); + } + + [Fact] + public void BZip2Stream_Compress_LeaveOpen_True() + { + using var innerStream = new TestStream(new MemoryStream()); + byte[] compressed; + using ( + var bzip2 = new BZip2Stream( + innerStream, + CompressionMode.Compress, + false, + leaveOpen: true + ) + ) + { + bzip2.Write(CreateTestData(), 0, CreateTestData().Length); + bzip2.Finish(); + } + + Assert.False( + innerStream.IsDisposed, + "Inner stream should NOT be disposed when leaveOpen=true" + ); + + // Should be able to read the compressed data + innerStream.Position = 0; + compressed = new byte[innerStream.Length]; + innerStream.Read(compressed, 0, compressed.Length); + Assert.True(compressed.Length > 0); + } + + [Fact] + public void BZip2Stream_Decompress_LeaveOpen_False() + { + // First compress some data + var memStream = new MemoryStream(); + using (var bzip2 = new BZip2Stream(memStream, CompressionMode.Compress, false, true)) + { + bzip2.Write(CreateTestData(), 0, CreateTestData().Length); + bzip2.Finish(); + } + + memStream.Position = 0; + using var innerStream = new TestStream(memStream); + var decompressed = new byte[CreateTestData().Length]; + + using ( + var bzip2 = new BZip2Stream( + innerStream, + CompressionMode.Decompress, + false, + leaveOpen: false + ) + ) + { + bzip2.Read(decompressed, 0, decompressed.Length); + } + + Assert.True(innerStream.IsDisposed, "Inner stream should be disposed when leaveOpen=false"); + Assert.Equal(CreateTestData(), decompressed); + } + + [Fact] + public void BZip2Stream_Decompress_LeaveOpen_True() + { + // First compress some data + var memStream = new MemoryStream(); + using (var bzip2 = new BZip2Stream(memStream, CompressionMode.Compress, false, true)) + { + bzip2.Write(CreateTestData(), 0, CreateTestData().Length); + bzip2.Finish(); + } + + memStream.Position = 0; + using var innerStream = new TestStream(memStream); + var decompressed = new byte[CreateTestData().Length]; + + using ( + var bzip2 = new BZip2Stream( + innerStream, + CompressionMode.Decompress, + false, + leaveOpen: true + ) + ) + { + bzip2.Read(decompressed, 0, decompressed.Length); + } + + Assert.False( + innerStream.IsDisposed, + "Inner stream should NOT be disposed when leaveOpen=true" + ); + Assert.Equal(CreateTestData(), decompressed); + + // Should still be able to use the stream + innerStream.Position = 0; + Assert.True(innerStream.CanRead); + } + + [Fact] + public void LZipStream_Compress_LeaveOpen_False() + { + using var innerStream = new TestStream(new MemoryStream()); + using (var lzip = new LZipStream(innerStream, CompressionMode.Compress, leaveOpen: false)) + { + lzip.Write(CreateTestData(), 0, CreateTestData().Length); + lzip.Finish(); + } + + Assert.True(innerStream.IsDisposed, "Inner stream should be disposed when leaveOpen=false"); + } + + [Fact] + public void LZipStream_Compress_LeaveOpen_True() + { + using var innerStream = new TestStream(new MemoryStream()); + byte[] compressed; + using (var lzip = new LZipStream(innerStream, CompressionMode.Compress, leaveOpen: true)) + { + lzip.Write(CreateTestData(), 0, CreateTestData().Length); + lzip.Finish(); + } + + Assert.False( + innerStream.IsDisposed, + "Inner stream should NOT be disposed when leaveOpen=true" + ); + + // Should be able to read the compressed data + innerStream.Position = 0; + compressed = new byte[innerStream.Length]; + innerStream.Read(compressed, 0, compressed.Length); + Assert.True(compressed.Length > 0); + } + + [Fact] + public void LZipStream_Decompress_LeaveOpen_False() + { + // First compress some data + var memStream = new MemoryStream(); + using (var lzip = new LZipStream(memStream, CompressionMode.Compress, true)) + { + lzip.Write(CreateTestData(), 0, CreateTestData().Length); + lzip.Finish(); + } + + memStream.Position = 0; + using var innerStream = new TestStream(memStream); + var decompressed = new byte[CreateTestData().Length]; + + using (var lzip = new LZipStream(innerStream, CompressionMode.Decompress, leaveOpen: false)) + { + lzip.Read(decompressed, 0, decompressed.Length); + } + + Assert.True(innerStream.IsDisposed, "Inner stream should be disposed when leaveOpen=false"); + Assert.Equal(CreateTestData(), decompressed); + } + + [Fact] + public void LZipStream_Decompress_LeaveOpen_True() + { + // First compress some data + var memStream = new MemoryStream(); + using (var lzip = new LZipStream(memStream, CompressionMode.Compress, true)) + { + lzip.Write(CreateTestData(), 0, CreateTestData().Length); + lzip.Finish(); + } + + memStream.Position = 0; + using var innerStream = new TestStream(memStream); + var decompressed = new byte[CreateTestData().Length]; + + using (var lzip = new LZipStream(innerStream, CompressionMode.Decompress, leaveOpen: true)) + { + lzip.Read(decompressed, 0, decompressed.Length); + } + + Assert.False( + innerStream.IsDisposed, + "Inner stream should NOT be disposed when leaveOpen=true" + ); + Assert.Equal(CreateTestData(), decompressed); + + // Should still be able to use the stream + innerStream.Position = 0; + Assert.True(innerStream.CanRead); + } +}