From 688f1f7036396c282191db05765d5a3baf3af3af Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 2 Nov 2026 13:27:58 +0000 Subject: [PATCH 1/2] Fix solid 7z Deflate entry extraction --- .../Archives/SevenZip/SevenZipArchive.cs | 8 +- src/SharpCompress/Common/EntryStream.Async.cs | 2 +- src/SharpCompress/Common/EntryStream.cs | 6 +- .../SevenZip/SevenZipDeflateSolidTests.cs | 122 ++++++++++++++++++ .../Archives/7Zip.solid.Deflate.7z | Bin 0 -> 33568 bytes 5 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs create mode 100644 tests/TestArchives/Archives/7Zip.solid.Deflate.7z diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index f90c7e92..b541f908 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -188,8 +188,12 @@ public partial class SevenZipArchive : AbstractArchive @@ -54,7 +56,7 @@ public partial class EntryStream : Stream } //Need a safe standard approach to this - it's okay for compression to overreads. Handling needs to be standardised - if (_stream is IStreamStack ss) + if (_rewindDecoderOnDispose && _stream is IStreamStack ss) { if ( ss.GetStream() diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs new file mode 100644 index 00000000..328c0f21 --- /dev/null +++ b/tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs @@ -0,0 +1,122 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using SharpCompress.Archives.SevenZip; +using SharpCompress.Readers; +using Xunit; + +namespace SharpCompress.Test.SevenZip; + +public class SevenZipDeflateSolidTests : TestBase +{ + private static readonly int[] Sizes = [1000, 70000, 100000, 200000, 500000, 1048576, 3000000]; + + // Generated with libarchive bsdtar --format 7zip --options compression=deflate. + // Each file repeats a 4096-byte xorshift sequence with a different starting offset. + private static byte[] CreateExpected(int index) + { + var block = new byte[4096]; + uint state = 0x12345678; + for (var i = 0; i < block.Length; i++) + { + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; + block[i] = (byte)state; + } + + var data = new byte[Sizes[index]]; + for (var i = 0; i < data.Length; i++) + { + data[i] = block[(i + index * 997) % block.Length]; + } + return data; + } + + [Theory] + [InlineData(512, 0)] + [InlineData(4096, 1)] + [InlineData(65536, 2)] + [InlineData(131072, 0)] + [InlineData(300000, 0)] + public void SevenZipArchive_Solid_Deflate_ExtractAllEntries(int bufferSize, int firstEntryMode) + { + using var archive = SevenZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.Deflate.7z") + ); + Assert.True(archive.IsSolid); + using var reader = archive.ExtractAllEntries(); + var index = 0; + while (reader.MoveToNextEntry()) + { + Assert.Equal($"{index}.bin", reader.Entry.Key); + Assert.Equal(Sizes[index], reader.Entry.Size); + if (index == 0 && firstEntryMode == 2) + { + index++; + continue; + } + + using var stream = reader.OpenEntryStream(); + if (index == 0 && firstEntryMode == 1) + { + Assert.True(stream.Read(new byte[17], 0, 17) > 0); + } + else + { + using var output = new MemoryStream(); + stream.CopyTo(output, bufferSize); + Assert.Equal(CreateExpected(index), output.ToArray()); + } + index++; + } + Assert.Equal(Sizes.Length, index); + } + + [Theory] + [InlineData(512, 0)] + [InlineData(4096, 1)] + [InlineData(65536, 2)] + [InlineData(131072, 0)] + [InlineData(300000, 0)] + public async Task SevenZipArchive_Solid_Deflate_ExtractAllEntries_Async( + int bufferSize, + int firstEntryMode + ) + { + await using var archive = await SevenZipArchive.OpenAsyncArchive( + Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.Deflate.7z"), + cancellationToken: CancellationToken.None + ); + Assert.True(await archive.IsSolidAsync()); + await using var reader = await archive.ExtractAllEntriesAsync(); + var index = 0; + while (await reader.MoveToNextEntryAsync(CancellationToken.None)) + { + Assert.Equal($"{index}.bin", reader.Entry.Key); + Assert.Equal(Sizes[index], reader.Entry.Size); + if (index == 0 && firstEntryMode == 2) + { + index++; + continue; + } + + await using var stream = await reader.OpenEntryStreamAsync(CancellationToken.None); + if (index == 0 && firstEntryMode == 1) + { + Assert.True( + await stream.ReadAsync(new byte[17], 0, 17, CancellationToken.None) > 0 + ); + } + else + { + using var output = new MemoryStream(); + await stream.CopyToAsync(output, bufferSize, CancellationToken.None); + Assert.Equal(CreateExpected(index), output.ToArray()); + } + index++; + } + Assert.Equal(Sizes.Length, index); + } +} diff --git a/tests/TestArchives/Archives/7Zip.solid.Deflate.7z b/tests/TestArchives/Archives/7Zip.solid.Deflate.7z new file mode 100644 index 0000000000000000000000000000000000000000..ab188ea76bc32dc8224cd77402dabf263ca27660 GIT binary patch literal 33568 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 literal 0 HcmV?d00001 From d5014d3a1c32cc61bb12b0a297e4c85c7a761ce3 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 2 Nov 2026 16:11:45 +0000 Subject: [PATCH 2/2] fix legacy test --- .../SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs index 328c0f21..085abf82 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs @@ -102,7 +102,11 @@ public class SevenZipDeflateSolidTests : TestBase continue; } +#if LEGACY_DOTNET + using var stream = await reader.OpenEntryStreamAsync(CancellationToken.None); +#else await using var stream = await reader.OpenEntryStreamAsync(CancellationToken.None); +#endif if (index == 0 && firstEntryMode == 1) { Assert.True(