From 72eaf66f055fc39077751e8cf4cfea5b26629123 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:53:53 +0000 Subject: [PATCH 1/6] Initial plan From db2f5c9cb9197e1bc566055e6cf16da08e67b05d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 17:01:18 +0000 Subject: [PATCH 2/6] Fix SevenZipReader to iterate entries as contiguous streams Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .../Archives/SevenZip/SevenZipArchive.cs | 54 +++++++++++++++---- .../SevenZip/SevenZipArchiveTests.cs | 24 +++++++++ tests/SharpCompress.Test/packages.lock.json | 12 +++++ 3 files changed, 79 insertions(+), 11 deletions(-) diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index d9b5ba1a..1d41224a 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -216,6 +216,9 @@ public class SevenZipArchive : AbstractArchive this._archive = archive; @@ -231,9 +234,10 @@ public class SevenZipArchive : AbstractArchive !x.IsDirectory)) { _currentEntry = entry; @@ -243,19 +247,47 @@ public class SevenZipArchive : AbstractArchive Date: Tue, 27 Jan 2026 17:03:20 +0000 Subject: [PATCH 3/6] Remove unused _currentFolderIndex field Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index 1d41224a..4ca1aa3d 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -218,7 +218,6 @@ public class SevenZipArchive : AbstractArchive this._archive = archive; @@ -261,7 +260,6 @@ public class SevenZipArchive : AbstractArchive Date: Tue, 27 Jan 2026 17:29:44 +0000 Subject: [PATCH 4/6] Add test to verify folder stream reuse in solid archives Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .../SevenZip/SevenZipArchiveTests.cs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs index e0690c15..c0f2d2ba 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Reflection; using SharpCompress.Archives; using SharpCompress.Archives.SevenZip; using SharpCompress.Common; @@ -275,4 +276,89 @@ public class SevenZipArchiveTests : ArchiveTests VerifyFiles(); } + + [Fact] + public void SevenZipArchive_Solid_VerifyStreamReuse() + { + // This test verifies that the folder stream is reused within each folder + // and not recreated for each entry in solid archives + var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z"); + using var archive = SevenZipArchive.Open(testArchive); + Assert.True(archive.IsSolid); + + using var reader = archive.ExtractAllEntries(); + + // Use reflection to access the private fields + var readerType = reader.GetType(); + var folderStreamField = readerType.GetField( + "_currentFolderStream", + BindingFlags.NonPublic | BindingFlags.Instance + ); + var currentFolderField = readerType.GetField( + "_currentFolder", + BindingFlags.NonPublic | BindingFlags.Instance + ); + + Assert.NotNull(folderStreamField); + Assert.NotNull(currentFolderField); + + Stream? currentFolderStreamInstance = null; + object? currentFolder = null; + var entryCount = 0; + var entriesInCurrentFolder = 0; + var streamRecreationsWithinFolder = 0; + + while (reader.MoveToNextEntry()) + { + if (!reader.Entry.IsDirectory) + { + // Extract the entry to trigger GetEntryStream + using var entryStream = reader.OpenEntryStream(); + var buffer = new byte[4096]; + while (entryStream.Read(buffer, 0, buffer.Length) > 0) + { + // Read the stream to completion + } + + entryCount++; + + // Get the current folder and folder stream via reflection + var folderStream = folderStreamField.GetValue(reader) as Stream; + var folder = currentFolderField.GetValue(reader); + + Assert.NotNull(folderStream); // Folder stream should exist + + // Check if we're in a new folder + if (currentFolder == null || !ReferenceEquals(currentFolder, folder)) + { + // Starting a new folder + currentFolder = folder; + currentFolderStreamInstance = folderStream; + entriesInCurrentFolder = 1; + } + else + { + // Same folder - verify stream wasn't recreated + entriesInCurrentFolder++; + + if (!ReferenceEquals(currentFolderStreamInstance, folderStream)) + { + // Stream was recreated within the same folder - this is the bug we're testing for! + streamRecreationsWithinFolder++; + } + + currentFolderStreamInstance = folderStream; + } + } + } + + // Verify we actually tested multiple entries + Assert.True(entryCount > 1, "Test should have multiple entries to verify stream reuse"); + + // The critical check: within a single folder, the stream should NEVER be recreated + Assert.Equal( + 0, + streamRecreationsWithinFolder + ); // Folder stream should remain the same for all entries in the same folder + } } From 8a67d501a8164f1580d0e556f5594d05f4833cbd Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Wed, 28 Jan 2026 08:10:06 +0000 Subject: [PATCH 5/6] Don't use reflection in tests --- .../Archives/SevenZip/SevenZipArchive.cs | 21 ++++++++++++++- .../SevenZip/SevenZipArchiveTests.cs | 26 ++++--------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index 4ca1aa3d..72787434 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -212,13 +212,32 @@ public class SevenZipArchive : AbstractArchive _database?._packSizes.Aggregate(0L, (total, packSize) => total + packSize) ?? 0; - private sealed class SevenZipReader : AbstractReader + internal sealed class SevenZipReader : AbstractReader { private readonly SevenZipArchive _archive; private SevenZipEntry? _currentEntry; private Stream? _currentFolderStream; private CFolder? _currentFolder; + /// + /// Enables internal diagnostics for tests. + /// When disabled (default), diagnostics properties return null to avoid exposing internal state. + /// + internal bool DiagnosticsEnabled { get; set; } + + /// + /// Current folder instance used to decide whether the solid folder stream should be reused. + /// Only available when is true. + /// + internal object? DiagnosticsCurrentFolder => DiagnosticsEnabled ? _currentFolder : null; + + /// + /// Current shared folder stream instance. + /// Only available when is true. + /// + internal Stream? DiagnosticsCurrentFolderStream => + DiagnosticsEnabled ? _currentFolderStream : null; + internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive) : base(readerOptions, ArchiveType.SevenZip) => this._archive = archive; diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs index c0f2d2ba..38c33eaa 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Linq; -using System.Reflection; using SharpCompress.Archives; using SharpCompress.Archives.SevenZip; using SharpCompress.Common; @@ -288,19 +287,8 @@ public class SevenZipArchiveTests : ArchiveTests using var reader = archive.ExtractAllEntries(); - // Use reflection to access the private fields - var readerType = reader.GetType(); - var folderStreamField = readerType.GetField( - "_currentFolderStream", - BindingFlags.NonPublic | BindingFlags.Instance - ); - var currentFolderField = readerType.GetField( - "_currentFolder", - BindingFlags.NonPublic | BindingFlags.Instance - ); - - Assert.NotNull(folderStreamField); - Assert.NotNull(currentFolderField); + var sevenZipReader = Assert.IsType(reader); + sevenZipReader.DiagnosticsEnabled = true; Stream? currentFolderStreamInstance = null; object? currentFolder = null; @@ -322,9 +310,8 @@ public class SevenZipArchiveTests : ArchiveTests entryCount++; - // Get the current folder and folder stream via reflection - var folderStream = folderStreamField.GetValue(reader) as Stream; - var folder = currentFolderField.GetValue(reader); + var folderStream = sevenZipReader.DiagnosticsCurrentFolderStream; + var folder = sevenZipReader.DiagnosticsCurrentFolder; Assert.NotNull(folderStream); // Folder stream should exist @@ -356,9 +343,6 @@ public class SevenZipArchiveTests : ArchiveTests Assert.True(entryCount > 1, "Test should have multiple entries to verify stream reuse"); // The critical check: within a single folder, the stream should NEVER be recreated - Assert.Equal( - 0, - streamRecreationsWithinFolder - ); // Folder stream should remain the same for all entries in the same folder + Assert.Equal(0, streamRecreationsWithinFolder); // Folder stream should remain the same for all entries in the same folder } } From 484bc740d7cbfc801e6cc2d92e2114faca0ba3a2 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Wed, 28 Jan 2026 08:26:28 +0000 Subject: [PATCH 6/6] Update src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index 72787434..43618dbc 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -271,8 +271,15 @@ public class SevenZipArchive : AbstractArchive