Compare commits

...

2 Commits

Author SHA1 Message Date
Adam Hathcock
8a54f253d5 Merge pull request #1200 from adamhathcock/adam/fix-async-7z-seeking 2026-02-11 12:35:18 +00:00
Adam Hathcock
d0baa16502 Fix 7z seeking to be contigous in async too 2026-02-11 12:16:19 +00:00
2 changed files with 98 additions and 5 deletions

View File

@@ -182,15 +182,15 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
);
}
// Wrap with SyncOnlyStream to work around LZMA async bugs
// Return a ReadOnlySubStream that reads from the shared folder stream
return CreateEntryStream(
new SyncOnlyStream(
new ReadOnlySubStream(_currentFolderStream, entry.Size, leaveOpen: true)
)
new ReadOnlySubStream(_currentFolderStream, entry.Size, leaveOpen: true)
);
}
protected override ValueTask<EntryStream> GetEntryStreamAsync(
CancellationToken cancellationToken = default
) => new(GetEntryStream());
public override void Dispose()
{
_currentFolderStream?.Dispose();

View File

@@ -3,6 +3,8 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Readers;
using SharpCompress.Test.Mocks;
using Xunit;
@@ -224,4 +226,95 @@ public class SevenZipArchiveAsyncTests : ArchiveTests
VerifyFiles();
}
[Fact]
public async Task SevenZipArchive_Solid_ExtractAllEntries_Contiguous_Async()
{
// This test verifies that solid archives iterate entries as contiguous streams
// rather than recreating the decompression stream for each entry
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z");
await using var archive = SevenZipArchive.OpenAsyncArchive(testArchive);
Assert.True(((SevenZipArchive)archive).IsSolid);
await using var reader = await archive.ExtractAllEntriesAsync();
while (await reader.MoveToNextEntryAsync())
{
if (!reader.Entry.IsDirectory)
{
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
}
}
VerifyFiles();
}
[Fact]
public async Task 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");
await using var archive = SevenZipArchive.OpenAsyncArchive(testArchive);
Assert.True(((SevenZipArchive)archive).IsSolid);
await using var reader = await archive.ExtractAllEntriesAsync();
var sevenZipReader = Assert.IsType<SevenZipArchive.SevenZipReader>(reader);
sevenZipReader.DiagnosticsEnabled = true;
Stream? currentFolderStreamInstance = null;
object? currentFolder = null;
var entryCount = 0;
var entriesInCurrentFolder = 0;
var streamRecreationsWithinFolder = 0;
while (await reader.MoveToNextEntryAsync())
{
if (!reader.Entry.IsDirectory)
{
// Extract the entry to trigger GetEntryStream
using var entryStream = await reader.OpenEntryStreamAsync();
var buffer = new byte[4096];
while (entryStream.Read(buffer, 0, buffer.Length) > 0)
{
// Read the stream to completion
}
entryCount++;
var folderStream = sevenZipReader.DiagnosticsCurrentFolderStream;
var folder = sevenZipReader.DiagnosticsCurrentFolder;
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
}
}