From b994f0ab55353490b2768f8a2f81bab7e8a2e8d7 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 29 Jan 2026 10:13:55 +0000 Subject: [PATCH] more 7z async --- .../Archives/SevenZip/SevenZipArchive.cs | 2 +- .../Archives/SevenZip/SevenZipArchiveEntry.cs | 3 +- .../Common/SevenZip/ArchiveDatabase.Async.cs | 40 ++ .../Common/SevenZip/ArchiveDatabase.cs | 4 +- .../Common/SevenZip/ArchiveReader.Async.cs | 432 +++++++++++++++++- .../Common/SevenZip/SevenZipFilePart.cs | 24 + .../Common/Zip/StreamingZipHeaderFactory.cs | 20 +- .../Compressors/LZMA/DecoderRegistry.Async.cs | 74 +++ .../Compressors/LZMA/DecoderStream.cs | 135 ++++++ .../Compressors/LZMA/LzmaStream.Async.cs | 2 +- .../Compressors/LZMA/Registry.cs | 2 +- src/SharpCompress/Readers/AbstractReader.cs | 9 +- tests/SharpCompress.Test/ArchiveTests.cs | 6 +- 13 files changed, 717 insertions(+), 36 deletions(-) create mode 100644 src/SharpCompress/Common/SevenZip/ArchiveDatabase.Async.cs create mode 100644 src/SharpCompress/Compressors/LZMA/DecoderRegistry.Async.cs diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index 66296b57..e9b27d6a 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -129,7 +129,7 @@ public partial class SevenZipArchive : AbstractArchive this._archive = archive; + : base(readerOptions, ArchiveType.SevenZip, false) => this._archive = archive; public override SevenZipVolume Volume => _archive.Volumes.Single(); diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs index 5b785e7c..b53fabe0 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs @@ -12,8 +12,7 @@ public class SevenZipArchiveEntry : SevenZipEntry, IArchiveEntry public Stream OpenEntryStream() => FilePart.GetCompressedStream(); - public ValueTask OpenEntryStreamAsync(CancellationToken cancellationToken = default) => - new(OpenEntryStream()); + public async ValueTask OpenEntryStreamAsync(CancellationToken cancellationToken = default) => (await FilePart.GetCompressedStreamAsync(cancellationToken)).NotNull(); public IArchive Archive { get; } diff --git a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.Async.cs b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.Async.cs new file mode 100644 index 00000000..e3f8cb9f --- /dev/null +++ b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.Async.cs @@ -0,0 +1,40 @@ +#nullable disable + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using SharpCompress.Compressors.LZMA; +using SharpCompress.Compressors.LZMA.Utilites; + +namespace SharpCompress.Common.SevenZip; + +internal sealed partial class ArchiveDatabase +{ + internal async ValueTask GetFolderStreamAsync( + Stream stream, + CFolder folder, + IPasswordProvider pw, + CancellationToken cancellationToken + ) + { + var packStreamIndex = folder._firstPackStreamId; + var folderStartPackPos = GetFolderStreamPos(folder, 0); + var count = folder._packStreams.Count; + var packSizes = new long[count]; + for (var j = 0; j < count; j++) + { + packSizes[j] = _packSizes[packStreamIndex + j]; + } + + return await DecoderStreamHelper + .CreateDecoderStreamAsync( + stream, + folderStartPackPos, + packSizes, + folder, + pw, + cancellationToken + ) + .ConfigureAwait(false); + } +} diff --git a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs index 4bac08ad..f4eb0851 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.Collections.Generic; @@ -8,7 +8,7 @@ using SharpCompress.Compressors.LZMA.Utilites; namespace SharpCompress.Common.SevenZip; -internal class ArchiveDatabase +internal partial class ArchiveDatabase { internal byte _majorVersion; internal byte _minorVersion; diff --git a/src/SharpCompress/Common/SevenZip/ArchiveReader.Async.cs b/src/SharpCompress/Common/SevenZip/ArchiveReader.Async.cs index 426a7a5c..d065a4bc 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveReader.Async.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveReader.Async.cs @@ -1,6 +1,7 @@ #nullable disable using System; +using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -125,10 +126,12 @@ internal sealed partial class ArchiveReader throw new InvalidOperationException(); } - var dataVector = ReadAndDecodePackedStreams( - db._startPositionAfterHeader, - db.PasswordProvider - ); + var dataVector = await ReadAndDecodePackedStreamsAsync( + db._startPositionAfterHeader, + db.PasswordProvider, + cancellationToken + ) + .ConfigureAwait(false); // compressed header without content is odd but ok if (dataVector.Count == 0) @@ -150,9 +153,428 @@ internal sealed partial class ArchiveReader } } - ReadHeader(db, db.PasswordProvider); + await ReadHeaderAsync(db, db.PasswordProvider, cancellationToken).ConfigureAwait(false); } db.Fill(); return db; } + + private async ValueTask> ReadAndDecodePackedStreamsAsync( + long baseOffset, + IPasswordProvider pass, + CancellationToken cancellationToken + ) + { +#if DEBUG + Log.WriteLine("-- ReadAndDecodePackedStreamsAsync --"); + Log.PushIndent(); +#endif + try + { + ReadStreamsInfo( + null, + out var dataStartPos, + out var packSizes, + out var packCrCs, + out var folders, + out var numUnpackStreamsInFolders, + out var unpackSizes, + out var digests + ); + + dataStartPos += baseOffset; + + var dataVector = new List(folders.Count); + var packIndex = 0; + foreach (var folder in folders) + { + var oldDataStartPos = dataStartPos; + var myPackSizes = new long[folder._packStreams.Count]; + for (var i = 0; i < myPackSizes.Length; i++) + { + var packSize = packSizes[packIndex + i]; + myPackSizes[i] = packSize; + dataStartPos += packSize; + } + + var outStream = await DecoderStreamHelper + .CreateDecoderStreamAsync( + _stream, + oldDataStartPos, + myPackSizes, + folder, + pass, + cancellationToken + ) + .ConfigureAwait(false); + + var unpackSize = checked((int)folder.GetUnpackSize()); + var data = new byte[unpackSize]; + await outStream + .ReadExactAsync(data, 0, data.Length, cancellationToken) + .ConfigureAwait(false); + if (outStream.ReadByte() >= 0) + { + throw new InvalidFormatException("Decoded stream is longer than expected."); + } + dataVector.Add(data); + + if (folder.UnpackCrcDefined) + { + if ( + Crc.Finish(Crc.Update(Crc.INIT_CRC, data, 0, unpackSize)) + != folder._unpackCrc + ) + { + throw new InvalidFormatException( + "Decoded stream does not match expected CRC." + ); + } + } + } + return dataVector; + } + finally + { +#if DEBUG + Log.PopIndent(); +#endif + } + } + + private async ValueTask ReadHeaderAsync( + ArchiveDatabase db, + IPasswordProvider getTextPassword, + CancellationToken cancellationToken + ) + { +#if DEBUG + Log.WriteLine("-- ReadHeaderAsync --"); + Log.PushIndent(); +#endif + try + { + var type = ReadId(); + + if (type == BlockType.ArchiveProperties) + { + ReadArchiveProperties(); + type = ReadId(); + } + + List dataVector = null; + if (type == BlockType.AdditionalStreamsInfo) + { + dataVector = await ReadAndDecodePackedStreamsAsync( + db._startPositionAfterHeader, + getTextPassword, + cancellationToken + ) + .ConfigureAwait(false); + type = ReadId(); + } + + List unpackSizes; + List digests; + + if (type == BlockType.MainStreamsInfo) + { + ReadStreamsInfo( + dataVector, + out db._dataStartPosition, + out db._packSizes, + out db._packCrCs, + out db._folders, + out db._numUnpackStreamsVector, + out unpackSizes, + out digests + ); + + db._dataStartPosition += db._startPositionAfterHeader; + type = ReadId(); + } + else + { + unpackSizes = new List(db._folders.Count); + digests = new List(db._folders.Count); + db._numUnpackStreamsVector = new List(db._folders.Count); + for (var i = 0; i < db._folders.Count; i++) + { + var folder = db._folders[i]; + unpackSizes.Add(folder.GetUnpackSize()); + digests.Add(folder._unpackCrc); + db._numUnpackStreamsVector.Add(1); + } + } + + db._files.Clear(); + + if (type == BlockType.End) + { + return; + } + + if (type != BlockType.FilesInfo) + { + throw new InvalidOperationException(); + } + + var numFiles = ReadNum(); +#if DEBUG + Log.WriteLine("NumFiles: " + numFiles); +#endif + db._files = new List(numFiles); + for (var i = 0; i < numFiles; i++) + { + db._files.Add(new CFileItem()); + } + + var emptyStreamVector = new BitVector(numFiles); + BitVector emptyFileVector = null; + BitVector antiFileVector = null; + var numEmptyStreams = 0; + + for (; ; ) + { + type = ReadId(); + if (type == BlockType.End) + { + break; + } + + var size = checked((long)ReadNumber()); + var oldPos = _currentReader.Offset; + switch (type) + { + case BlockType.Name: + using (var streamSwitch = new CStreamSwitch()) + { + streamSwitch.Set(this, dataVector); +#if DEBUG + Log.Write("FileNames:"); +#endif + for (var i = 0; i < db._files.Count; i++) + { + db._files[i].Name = _currentReader.ReadString(); +#if DEBUG + Log.Write(" " + db._files[i].Name); +#endif + } +#if DEBUG + Log.WriteLine(); +#endif + } + break; + case BlockType.WinAttributes: +#if DEBUG + Log.Write("WinAttributes:"); +#endif + ReadAttributeVector( + dataVector, + numFiles, + delegate(int i, uint? attr) + { + db._files[i].ExtendedAttrib = attr; + + if (attr.HasValue && (attr.Value >> 16) != 0) + { + attr = attr.Value & 0x7FFFu; + } + + db._files[i].Attrib = attr; +#if DEBUG + Log.Write( + " " + (attr.HasValue ? attr.Value.ToString("x8") : "n/a") + ); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.EmptyStream: + emptyStreamVector = ReadBitVector(numFiles); +#if DEBUG + + Log.Write("EmptyStream: "); +#endif + for (var i = 0; i < emptyStreamVector.Length; i++) + { + if (emptyStreamVector[i]) + { +#if DEBUG + Log.Write("x"); +#endif + numEmptyStreams++; + } + else + { +#if DEBUG + Log.Write("."); +#endif + } + } +#if DEBUG + Log.WriteLine(); +#endif + + emptyFileVector = new BitVector(numEmptyStreams); + antiFileVector = new BitVector(numEmptyStreams); + break; + case BlockType.EmptyFile: + emptyFileVector = ReadBitVector(numEmptyStreams); +#if DEBUG + Log.Write("EmptyFile: "); + for (var i = 0; i < numEmptyStreams; i++) + { + Log.Write(emptyFileVector[i] ? "x" : "."); + } + Log.WriteLine(); +#endif + break; + case BlockType.Anti: + antiFileVector = ReadBitVector(numEmptyStreams); +#if DEBUG + Log.Write("Anti: "); + for (var i = 0; i < numEmptyStreams; i++) + { + Log.Write(antiFileVector[i] ? "x" : "."); + } + Log.WriteLine(); +#endif + break; + case BlockType.StartPos: +#if DEBUG + Log.Write("StartPos:"); +#endif + ReadNumberVector( + dataVector, + numFiles, + delegate(int i, long? startPos) + { + db._files[i].StartPos = startPos; +#if DEBUG + Log.Write( + " " + (startPos.HasValue ? startPos.Value.ToString() : "n/a") + ); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.CTime: +#if DEBUG + Log.Write("CTime:"); +#endif + ReadDateTimeVector( + dataVector, + numFiles, + delegate(int i, DateTime? time) + { + db._files[i].CTime = time; +#if DEBUG + Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a")); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.ATime: +#if DEBUG + Log.Write("ATime:"); +#endif + ReadDateTimeVector( + dataVector, + numFiles, + delegate(int i, DateTime? time) + { + db._files[i].ATime = time; +#if DEBUG + Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a")); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.MTime: +#if DEBUG + Log.Write("MTime:"); +#endif + ReadDateTimeVector( + dataVector, + numFiles, + delegate(int i, DateTime? time) + { + db._files[i].MTime = time; +#if DEBUG + Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a")); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.Dummy: +#if DEBUG + Log.Write("Dummy: " + size); +#endif + for (long j = 0; j < size; j++) + { + if (ReadByte() != 0) + { + throw new InvalidOperationException(); + } + } + break; + default: + SkipData(size); + break; + } + + var checkRecordsSize = (db._majorVersion > 0 || db._minorVersion > 2); + if (checkRecordsSize && _currentReader.Offset - oldPos != size) + { + throw new InvalidOperationException(); + } + } + + var emptyFileIndex = 0; + var sizeIndex = 0; + for (var i = 0; i < numFiles; i++) + { + var file = db._files[i]; + file.HasStream = !emptyStreamVector[i]; + if (file.HasStream) + { + file.IsDir = false; + file.IsAnti = false; + file.Size = unpackSizes[sizeIndex]; + file.Crc = digests[sizeIndex]; + sizeIndex++; + } + else + { + file.IsDir = !emptyFileVector[emptyFileIndex]; + file.IsAnti = antiFileVector[emptyFileIndex]; + emptyFileIndex++; + file.Size = 0; + file.Crc = null; + } + } + } + finally + { +#if DEBUG + Log.PopIndent(); +#endif + } + } } diff --git a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs index c41fd698..cecd33e4 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs @@ -1,5 +1,7 @@ using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.IO; namespace SharpCompress.Common.SevenZip; @@ -58,6 +60,28 @@ internal class SevenZipFilePart : FilePart return new ReadOnlySubStream(folderStream, Header.Size, leaveOpen: false); } + internal override async ValueTask GetCompressedStreamAsync(CancellationToken cancellationToken = default) + { + if (!Header.HasStream) + { + return Stream.Null; + } + var folderStream = await _database.GetFolderStreamAsync(_stream, Folder!, _database.PasswordProvider, cancellationToken); + + var firstFileIndex = _database._folderStartFileIndex[_database._folders.IndexOf(Folder!)]; + var skipCount = Index - firstFileIndex; + long skipSize = 0; + for (var i = 0; i < skipCount; i++) + { + skipSize += _database._files[firstFileIndex + i].Size; + } + if (skipSize > 0) + { + await folderStream.SkipAsync(skipSize, cancellationToken); + } + return new ReadOnlySubStream(folderStream, Header.Size, leaveOpen: false); + } + public CompressionType CompressionType { get diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs index 0bbbcbcc..bf6c4be8 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs @@ -23,24 +23,8 @@ internal sealed partial class StreamingZipHeaderFactory : ZipHeaderFactory internal IEnumerable ReadStreamHeader(Stream stream) { - if (stream is not SharpCompressStream) //ensure the stream is already a SharpCompressStream. So the buffer/size will already be set - { - //the original code wrapped this with RewindableStream. Wrap with SharpCompressStream as we can get the buffer size - if (stream is SourceStream src) - { - stream = new SharpCompressStream( - stream, - src.ReaderOptions.LeaveStreamOpen, - bufferSize: src.ReaderOptions.BufferSize - ); - } - else - { - throw new ArgumentException("Stream must be a SharpCompressStream", nameof(stream)); - } - } - var rewindableStream = (SharpCompressStream)stream; - + //the original code wrapped this with RewindableStream. Wrap with SharpCompressStream as we can get the buffer size + var rewindableStream = stream; while (true) { var reader = new BinaryReader(rewindableStream); diff --git a/src/SharpCompress/Compressors/LZMA/DecoderRegistry.Async.cs b/src/SharpCompress/Compressors/LZMA/DecoderRegistry.Async.cs new file mode 100644 index 00000000..d94d7acc --- /dev/null +++ b/src/SharpCompress/Compressors/LZMA/DecoderRegistry.Async.cs @@ -0,0 +1,74 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using SharpCompress.Common.SevenZip; +using SharpCompress.Compressors.BZip2; +using SharpCompress.Compressors.Deflate; +using SharpCompress.Compressors.Filters; +using SharpCompress.Compressors.LZMA.Utilites; +using SharpCompress.Compressors.PPMd; +using SharpCompress.Compressors.ZStandard; + +namespace SharpCompress.Compressors.LZMA; + +internal static partial class DecoderRegistry +{ + internal static async ValueTask CreateDecoderStreamAsync( + CMethodId id, + Stream[] inStreams, + byte[] info, + IPasswordProvider pass, + long limit, + CancellationToken cancellationToken + ) + { + switch (id._id) + { + case K_COPY: + if (info != null) + { + throw new NotSupportedException(); + } + return inStreams.Single(); + case K_DELTA: + return new DeltaFilter(false, inStreams.Single(), info); + case K_LZMA: + case K_LZMA2: + return await LzmaStream + .CreateAsync(info, inStreams.Single(), -1, limit, null, info.Length < 5, false) + .ConfigureAwait(false); + case CMethodId.K_AES_ID: + return new AesDecoderStream(inStreams.Single(), info, pass, limit); + case K_BCJ: + return new BCJFilter(false, inStreams.Single()); + case K_BCJ2: + return new Bcj2DecoderStream(inStreams, info, limit); + case K_PPC: + return new BCJFilterPPC(false, inStreams.Single()); + case K_IA64: + return new BCJFilterIA64(false, inStreams.Single()); + case K_ARM: + return new BCJFilterARM(false, inStreams.Single()); + case K_ARMT: + return new BCJFilterARMT(false, inStreams.Single()); + case K_SPARC: + return new BCJFilterSPARC(false, inStreams.Single()); + case K_ARM64: + return new BCJFilterARM64(false, inStreams.Single()); + case K_RISCV: + return new BCJFilterRISCV(false, inStreams.Single()); + case K_B_ZIP2: + return await BZip2Stream.CreateAsync(inStreams.Single(), CompressionMode.Decompress, true, cancellationToken: cancellationToken); + case K_PPMD: + return new PpmdStream(new PpmdProperties(info), inStreams.Single(), false); + case K_DEFLATE: + return new DeflateStream(inStreams.Single(), CompressionMode.Decompress); + case K_ZSTD: + return new DecompressionStream(inStreams.Single()); + default: + throw new NotSupportedException(); + } + } +} diff --git a/src/SharpCompress/Compressors/LZMA/DecoderStream.cs b/src/SharpCompress/Compressors/LZMA/DecoderStream.cs index b54d89e3..b758cd6c 100644 --- a/src/SharpCompress/Compressors/LZMA/DecoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/DecoderStream.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.SevenZip; using SharpCompress.Compressors.LZMA.Utilites; @@ -180,6 +182,99 @@ internal static class DecoderStreamHelper ); } + private static async ValueTask CreateDecoderStreamAsync( + Stream[] packStreams, + long[] packSizes, + Stream[] outStreams, + CFolder folderInfo, + int coderIndex, + IPasswordProvider pass, + CancellationToken cancellationToken + ) + { + var coderInfo = folderInfo._coders[coderIndex]; + if (coderInfo._numOutStreams != 1) + { + throw new NotSupportedException("Multiple output streams are not supported."); + } + + var inStreamId = 0; + for (var i = 0; i < coderIndex; i++) + { + inStreamId += folderInfo._coders[i]._numInStreams; + } + + var outStreamId = 0; + for (var i = 0; i < coderIndex; i++) + { + outStreamId += folderInfo._coders[i]._numOutStreams; + } + + var inStreams = new Stream[coderInfo._numInStreams]; + + for (var i = 0; i < inStreams.Length; i++, inStreamId++) + { + var bindPairIndex = folderInfo.FindBindPairForInStream(inStreamId); + if (bindPairIndex >= 0) + { + var pairedOutIndex = folderInfo._bindPairs[bindPairIndex]._outIndex; + + if (outStreams[pairedOutIndex] != null) + { + throw new NotSupportedException( + "Overlapping stream bindings are not supported." + ); + } + + var otherCoderIndex = FindCoderIndexForOutStreamIndex(folderInfo, pairedOutIndex); + inStreams[i] = await CreateDecoderStreamAsync( + packStreams, + packSizes, + outStreams, + folderInfo, + otherCoderIndex, + pass, + cancellationToken + ) + .ConfigureAwait(false); + + //inStreamSizes[i] = folderInfo.UnpackSizes[pairedOutIndex]; + + if (outStreams[pairedOutIndex] != null) + { + throw new NotSupportedException( + "Overlapping stream bindings are not supported." + ); + } + + outStreams[pairedOutIndex] = inStreams[i]; + } + else + { + var index = folderInfo.FindPackStreamArrayIndex(inStreamId); + if (index < 0) + { + throw new NotSupportedException("Could not find input stream binding."); + } + + inStreams[i] = packStreams[index]; + + //inStreamSizes[i] = packSizes[index]; + } + } + + var unpackSize = folderInfo._unpackSizes[outStreamId]; + return await DecoderRegistry.CreateDecoderStreamAsync( + coderInfo._methodId, + inStreams, + coderInfo._props, + pass, + unpackSize, + cancellationToken + ) + .ConfigureAwait(false); + } + internal static Stream CreateDecoderStream( Stream inStream, long startPos, @@ -216,4 +311,44 @@ internal static class DecoderStreamHelper pass ); } + + internal static async ValueTask CreateDecoderStreamAsync( + Stream inStream, + long startPos, + long[] packSizes, + CFolder folderInfo, + IPasswordProvider pass, + CancellationToken cancellationToken + ) + { + if (!folderInfo.CheckStructure()) + { + throw new NotSupportedException("Unsupported stream binding structure."); + } + + var inStreams = new Stream[folderInfo._packStreams.Count]; + for (var j = 0; j < folderInfo._packStreams.Count; j++) + { + inStreams[j] = new BufferedSubStream(inStream, startPos, packSizes[j]); + startPos += packSizes[j]; + } + + var outStreams = new Stream[folderInfo._unpackSizes.Count]; + + FindPrimaryOutStreamIndex( + folderInfo, + out var primaryCoderIndex, + out var primaryOutStreamIndex + ); + return await CreateDecoderStreamAsync( + inStreams, + packSizes, + outStreams, + folderInfo, + primaryCoderIndex, + pass, + cancellationToken + ) + .ConfigureAwait(false); + } } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs index 10abaaec..406e7637 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs @@ -130,7 +130,7 @@ public partial class LzmaStream _decoder.SetDecoderProperties(Properties); } - _rangeDecoder.Init(_inputStream); + await _rangeDecoder.InitAsync(_inputStream, cancellationToken); } else if (control > 0x02) { diff --git a/src/SharpCompress/Compressors/LZMA/Registry.cs b/src/SharpCompress/Compressors/LZMA/Registry.cs index 8de8f841..8cd1747c 100644 --- a/src/SharpCompress/Compressors/LZMA/Registry.cs +++ b/src/SharpCompress/Compressors/LZMA/Registry.cs @@ -11,7 +11,7 @@ using SharpCompress.Compressors.ZStandard; namespace SharpCompress.Compressors.LZMA; -internal static class DecoderRegistry +internal static partial class DecoderRegistry { private const uint K_COPY = 0x0; private const uint K_DELTA = 0x3; diff --git a/src/SharpCompress/Readers/AbstractReader.cs b/src/SharpCompress/Readers/AbstractReader.cs index 7fcc2ab0..e6898b52 100644 --- a/src/SharpCompress/Readers/AbstractReader.cs +++ b/src/SharpCompress/Readers/AbstractReader.cs @@ -18,10 +18,12 @@ public abstract partial class AbstractReader : IReader, IAsyncR private IEnumerator? _entriesForCurrentReadStream; private IAsyncEnumerator? _entriesForCurrentReadStreamAsync; private bool _wroteCurrentEntry; + private readonly bool _disposeVolume; - internal AbstractReader(ReaderOptions options, ArchiveType archiveType) + internal AbstractReader(ReaderOptions options, ArchiveType archiveType, bool disposeVolume = true) { ArchiveType = archiveType; + _disposeVolume = disposeVolume; Options = options; } @@ -56,7 +58,10 @@ public abstract partial class AbstractReader : IReader, IAsyncR public virtual void Dispose() { _entriesForCurrentReadStream?.Dispose(); - Volume?.Dispose(); + if (_disposeVolume) + { + Volume?.Dispose(); + } } #endregion diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 36128815..68732364 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -44,10 +44,8 @@ public class ArchiveTests : ReaderTests foreach (var path in testArchives) { using ( - var stream = SharpCompressStream.Create( - File.OpenRead(path), - leaveOpen: true, - throwOnDispose: true + var stream = new NonDisposingStream( + File.OpenRead(path) ) ) {