From a62a73dc3eb26850e331b084aa2003401027186d Mon Sep 17 00:00:00 2001 From: fartwhif Date: Sat, 8 Sep 2018 00:38:21 -0400 Subject: [PATCH 1/2] brought a direct stream getter delegate out made the stream classes public changed the signature for the "extract all files with callback" function removed garbage collection bossyness --- SevenZip/ArchiveExtractCallback.cs | 36 ++++++++++----- SevenZip/ArchiveOpenCallback.cs | 2 +- SevenZip/ArchiveUpdateCallback.cs | 8 ++-- SevenZip/Common.cs | 2 +- SevenZip/SevenZip.csproj | 3 ++ SevenZip/SevenZipExtractor.cs | 56 +++++++++++++++++++---- SevenZip/SevenZipExtractorAsynchronous.cs | 5 +- SevenZip/StreamWrappers.cs | 12 ++--- 8 files changed, 88 insertions(+), 36 deletions(-) diff --git a/SevenZip/ArchiveExtractCallback.cs b/SevenZip/ArchiveExtractCallback.cs index fb5a578..6dea1b8 100644 --- a/SevenZip/ArchiveExtractCallback.cs +++ b/SevenZip/ArchiveExtractCallback.cs @@ -36,6 +36,7 @@ internal sealed class ArchiveExtractCallback : CallbackBase, IArchiveExtractCall private OutStreamWrapper _fileStream; private bool _directoryStructure; private int _currentIndex; + private Func _getStream; const int MEMORY_PRESSURE = 64 * 1024 * 1024; //64mb seems to be the maximum value #region Constructors @@ -49,9 +50,10 @@ internal sealed class ArchiveExtractCallback : CallbackBase, IArchiveExtractCall /// The owner of the callback /// The list of actual indexes (solid archives support) /// The value indicating whether to preserve directory structure of extracted files. - public ArchiveExtractCallback(IInArchive archive, string directory, int filesCount, bool directoryStructure, + public ArchiveExtractCallback(Func getStream, IInArchive archive, string directory, int filesCount, bool directoryStructure, List actualIndexes, SevenZipExtractor extractor) { + this._getStream = getStream; Init(archive, directory, filesCount, directoryStructure, actualIndexes, extractor); } @@ -65,10 +67,11 @@ public ArchiveExtractCallback(IInArchive archive, string directory, int filesCou /// The owner of the callback /// The list of actual indexes (solid archives support) /// The value indicating whether to preserve directory structure of extracted files. - public ArchiveExtractCallback(IInArchive archive, string directory, int filesCount, bool directoryStructure, + public ArchiveExtractCallback(Func getStream, IInArchive archive, string directory, int filesCount, bool directoryStructure, List actualIndexes, string password, SevenZipExtractor extractor) : base(password) { + this._getStream = getStream; Init(archive, directory, filesCount, directoryStructure, actualIndexes, extractor); } @@ -80,9 +83,10 @@ public ArchiveExtractCallback(IInArchive archive, string directory, int filesCou /// The archive files count /// The file index for the stream /// The owner of the callback - public ArchiveExtractCallback(IInArchive archive, Stream stream, int filesCount, uint fileIndex, + public ArchiveExtractCallback(Func getStream, IInArchive archive, Stream stream, int filesCount, uint fileIndex, SevenZipExtractor extractor) { + this._getStream = getStream; Init(archive, stream, filesCount, fileIndex, extractor); } @@ -95,10 +99,11 @@ public ArchiveExtractCallback(IInArchive archive, Stream stream, int filesCount, /// The file index for the stream /// Password for the archive /// The owner of the callback - public ArchiveExtractCallback(IInArchive archive, Stream stream, int filesCount, uint fileIndex, string password, + public ArchiveExtractCallback(Func getStream, IInArchive archive, Stream stream, int filesCount, uint fileIndex, string password, SevenZipExtractor extractor) : base(password) { + this._getStream = getStream; Init(archive, stream, filesCount, fileIndex, extractor); } @@ -130,7 +135,7 @@ private void CommonInit(IInArchive archive, int filesCount, SevenZipExtractor ex _fakeStream = new FakeOutStreamWrapper(); _fakeStream.BytesWritten += IntEventArgsHandler; _extractor = extractor; - GC.AddMemoryPressure(MEMORY_PRESSURE); + //GC.AddMemoryPressure(MEMORY_PRESSURE); } #endregion @@ -382,14 +387,21 @@ public int GetStream(uint index, out ISequentialOutStream outStream, AskMode ask { #region Extraction to a stream - if (index == _fileIndex) + if (_getStream == null) { - outStream = _fileStream; - _fileIndex = null; + if (index == _fileIndex) + { + outStream = _fileStream; + _fileIndex = null; + } + else + { + outStream = _fakeStream; + } } else { - outStream = _fakeStream; + outStream = _getStream(); } #endregion @@ -458,8 +470,8 @@ public void SetOperationResult(OperationResult operationResult) } catch (ObjectDisposedException) { } _fileStream = null; - GC.Collect(); - GC.WaitForPendingFinalizers(); + //GC.Collect(); + //GC.WaitForPendingFinalizers(); } var iea = new FileInfoEventArgs( _extractor.ArchiveFileData[_currentIndex], PercentDoneEventArgs.ProducePercentDone(_doneRate)); @@ -492,7 +504,7 @@ public int CryptoGetTextPassword(out string password) public void Dispose() { - GC.RemoveMemoryPressure(MEMORY_PRESSURE); + //GC.RemoveMemoryPressure(MEMORY_PRESSURE); if (_fileStream != null) { diff --git a/SevenZip/ArchiveOpenCallback.cs b/SevenZip/ArchiveOpenCallback.cs index 323b775..210e94d 100644 --- a/SevenZip/ArchiveOpenCallback.cs +++ b/SevenZip/ArchiveOpenCallback.cs @@ -187,7 +187,7 @@ public void Dispose() _wrappers = null; } - GC.SuppressFinalize(this); + //GC.SuppressFinalize(this); } #endregion diff --git a/SevenZip/ArchiveUpdateCallback.cs b/SevenZip/ArchiveUpdateCallback.cs index 4d85517..a1f78dc 100644 --- a/SevenZip/ArchiveUpdateCallback.cs +++ b/SevenZip/ArchiveUpdateCallback.cs @@ -265,7 +265,7 @@ public float DictionarySize set { _memoryPressure = (int)(value * 1024 * 1024); - GC.AddMemoryPressure(_memoryPressure); + //GC.AddMemoryPressure(_memoryPressure); } } @@ -707,7 +707,7 @@ public void SetOperationResult(OperationResult operationResult) _wrappersToDispose.Add(_fileStream); } _fileStream = null; - GC.Collect(); + //GC.Collect(); // Issue #6987 //GC.WaitForPendingFinalizers(); } @@ -731,7 +731,7 @@ public int CryptoGetTextPassword2(ref int passwordIsDefined, out string password public void Dispose() { - GC.RemoveMemoryPressure(_memoryPressure); + //GC.RemoveMemoryPressure(_memoryPressure); if (_fileStream != null) { @@ -754,7 +754,7 @@ public void Dispose() } } - GC.SuppressFinalize(this); + //GC.SuppressFinalize(this); } #endregion diff --git a/SevenZip/Common.cs b/SevenZip/Common.cs index 6b0ee9d..4701489 100644 --- a/SevenZip/Common.cs +++ b/SevenZip/Common.cs @@ -64,7 +64,7 @@ internal virtual void ReleaseContext() { Context = null; NeedsToBeRecreated = true; - GC.SuppressFinalize(this); + //GC.SuppressFinalize(this); } private delegate void EventHandlerDelegate(EventHandler handler, T e) where T : EventArgs; diff --git a/SevenZip/SevenZip.csproj b/SevenZip/SevenZip.csproj index a8ca41c..fdcb7d1 100644 --- a/SevenZip/SevenZip.csproj +++ b/SevenZip/SevenZip.csproj @@ -75,6 +75,9 @@ prompt false + + true + diff --git a/SevenZip/SevenZipExtractor.cs b/SevenZip/SevenZipExtractor.cs index 28acf2f..e882c60 100644 --- a/SevenZip/SevenZipExtractor.cs +++ b/SevenZip/SevenZipExtractor.cs @@ -643,8 +643,8 @@ private ArchiveExtractCallback GetArchiveExtractCallback(string directory, int f List actualIndexes) { var aec = String.IsNullOrEmpty(Password) - ? new ArchiveExtractCallback(_archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, this) - : new ArchiveExtractCallback(_archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, Password, this); + ? new ArchiveExtractCallback(null, _archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, this) + : new ArchiveExtractCallback(null, _archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, Password, this); ArchiveExtractCallbackCommonInit(aec); return aec; } @@ -656,11 +656,11 @@ private ArchiveExtractCallback GetArchiveExtractCallback(string directory, int f /// The file index /// The number of files to be extracted /// The ArchiveExtractCallback callback - private ArchiveExtractCallback GetArchiveExtractCallback(Stream stream, uint index, int filesCount) + private ArchiveExtractCallback GetArchiveExtractCallback(Func getStream, Stream stream, uint index, int filesCount) { var aec = String.IsNullOrEmpty(Password) - ? new ArchiveExtractCallback(_archive, stream, filesCount, index, this) - : new ArchiveExtractCallback(_archive, stream, filesCount, index, Password, this); + ? new ArchiveExtractCallback(getStream, _archive, stream, filesCount, index, this) + : new ArchiveExtractCallback(getStream, _archive, stream, filesCount, index, Password, this); ArchiveExtractCallbackCommonInit(aec); return aec; } @@ -767,7 +767,7 @@ public void Dispose() CommonDispose(); } _disposed = true; - GC.SuppressFinalize(this); + //GC.SuppressFinalize(this); } #endregion @@ -1033,7 +1033,7 @@ public void ExtractFile(int index, Stream stream) } try { - using (var aec = GetArchiveExtractCallback(stream, (uint) index, indexes.Length)) + using (var aec = GetArchiveExtractCallback(null, stream, (uint) index, indexes.Length)) { try { @@ -1203,13 +1203,49 @@ public void ExtractFiles(string directory, params string[] fileNames) /// 7-Zip (and any other solid) archives are NOT supported. /// /// The callback to call for each file in the archive. - public void ExtractFiles(ExtractFileCallback extractFileCallback) + public void ExtractFiles(ExtractFileCallback extractFileCallback, Func getStream = null, Action FileExtractStart = null, Action FileExtractComplete = null) { DisposedCheck(); InitArchiveFileData(false); - if (IsSolid) + if (IsSolid && getStream != null) { - // solid strategy + DisposedCheck(); + ClearExceptions(); + //var indexes = Enumerable.Range(0, ArchiveFileData.Max(k => k.Index)).Select(k=>(uint)k).ToArray(); + var indexes = ArchiveFileData.Select(k => (uint)k.Index).ToArray(); + var archiveStream = GetArchiveStream(false); + var openCallback = GetArchiveOpenCallback(); + if (!OpenArchive(archiveStream, openCallback)) + { + return; + } + try + { + using (var aec = GetArchiveExtractCallback(getStream, null, (uint)0, indexes.Length)) + { + aec.FileExtractionStarted += (src, fi) => { FileExtractStart?.Invoke(fi); }; + aec.FileExtractionFinished += (src, fi) => { FileExtractComplete?.Invoke(fi); }; + try + { + CheckedExecute( + _archive.Extract(indexes, (uint)indexes.Length, 0, aec), + SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec); + } + finally + { + FreeArchiveExtractCallback(aec); + } + } + } + catch (Exception) + { + if (openCallback.ThrowException()) + { + throw; + } + } + OnEvent(ExtractionFinished, EventArgs.Empty, false); + ThrowUserException(); } else { diff --git a/SevenZip/SevenZipExtractorAsynchronous.cs b/SevenZip/SevenZipExtractorAsynchronous.cs index c9c5803..bd79d79 100644 --- a/SevenZip/SevenZipExtractorAsynchronous.cs +++ b/SevenZip/SevenZipExtractorAsynchronous.cs @@ -93,6 +93,7 @@ internal override void ReleaseContext() /// /// The callback to call for each file in the archive. private delegate void ExtractFiles3Delegate(ExtractFileCallback extractFileCallback); + private delegate void ExtractFiles4Delegate(ExtractFileCallback extractFileCallback, Func getStream, Action FileExtractStart, Action FileExtractComplete); #endregion /// @@ -155,10 +156,10 @@ public void BeginExtractFiles(string directory, params string[] fileNames) /// 7-Zip (and any other solid) archives are NOT supported. /// /// The callback to call for each file in the archive. - public void BeginExtractFiles(ExtractFileCallback extractFileCallback) + public void BeginExtractFiles(ExtractFileCallback extractFileCallback, Func getStream = null, Action FileExtractStart = null, Action FileExtractComplete = null) { SaveContext(); - new ExtractFiles3Delegate(ExtractFiles).BeginInvoke(extractFileCallback, AsyncCallbackImplementation, this); + new ExtractFiles4Delegate(ExtractFiles).BeginInvoke(extractFileCallback, getStream, FileExtractStart, FileExtractComplete, AsyncCallbackImplementation, this); } } } diff --git a/SevenZip/StreamWrappers.cs b/SevenZip/StreamWrappers.cs index fe79610..02a48b2 100644 --- a/SevenZip/StreamWrappers.cs +++ b/SevenZip/StreamWrappers.cs @@ -11,7 +11,7 @@ namespace SevenZip /// /// A class that has DisposeStream property. /// - internal class DisposeVariableWrapper + public class DisposeVariableWrapper { public bool DisposeStream { protected get; set; } @@ -21,7 +21,7 @@ internal class DisposeVariableWrapper /// /// Stream wrapper used in InStreamWrapper /// - internal class StreamWrapper : DisposeVariableWrapper, IDisposable + public class StreamWrapper : DisposeVariableWrapper, IDisposable { /// /// File name associated with the stream (for date fix) @@ -94,7 +94,7 @@ public void Dispose() catch (ArgumentOutOfRangeException) {} } - GC.SuppressFinalize(this); + //GC.SuppressFinalize(this); } #endregion @@ -162,7 +162,7 @@ private void OnBytesRead(IntEventArgs e) /// /// IOutStream wrapper used in stream write operations. /// - internal sealed class OutStreamWrapper : StreamWrapper, ISequentialOutStream, IOutStream + public sealed class OutStreamWrapper : StreamWrapper, ISequentialOutStream, IOutStream { /// /// Initializes a new instance of the OutStreamWrapper class @@ -268,7 +268,7 @@ public virtual void Dispose() } Streams.Clear(); } - GC.SuppressFinalize(this); + //GC.SuppressFinalize(this); } #endregion @@ -459,7 +459,7 @@ internal sealed class FakeOutStreamWrapper : ISequentialOutStream, IDisposable public void Dispose() { - GC.SuppressFinalize(this); + //GC.SuppressFinalize(this); } #endregion From d00a6fcda63315c584d94122d93cd4828ef5e9e1 Mon Sep 17 00:00:00 2001 From: fartwhif Date: Mon, 17 Sep 2018 21:43:27 -0400 Subject: [PATCH 2/2] further optimizations for large solid archives --- SevenZip/ArchiveExtractCallback.cs | 16 +- SevenZip/COM.cs | 2 +- SevenZip/SevenZipExtractor.cs | 298 ++++++++++++++++------ SevenZip/SevenZipExtractorAsynchronous.cs | 6 +- 4 files changed, 233 insertions(+), 89 deletions(-) diff --git a/SevenZip/ArchiveExtractCallback.cs b/SevenZip/ArchiveExtractCallback.cs index 6dea1b8..b38c454 100644 --- a/SevenZip/ArchiveExtractCallback.cs +++ b/SevenZip/ArchiveExtractCallback.cs @@ -36,7 +36,7 @@ internal sealed class ArchiveExtractCallback : CallbackBase, IArchiveExtractCall private OutStreamWrapper _fileStream; private bool _directoryStructure; private int _currentIndex; - private Func _getStream; + private Func _getStream; const int MEMORY_PRESSURE = 64 * 1024 * 1024; //64mb seems to be the maximum value #region Constructors @@ -50,7 +50,7 @@ internal sealed class ArchiveExtractCallback : CallbackBase, IArchiveExtractCall /// The owner of the callback /// The list of actual indexes (solid archives support) /// The value indicating whether to preserve directory structure of extracted files. - public ArchiveExtractCallback(Func getStream, IInArchive archive, string directory, int filesCount, bool directoryStructure, + public ArchiveExtractCallback(Func getStream, IInArchive archive, string directory, int filesCount, bool directoryStructure, List actualIndexes, SevenZipExtractor extractor) { this._getStream = getStream; @@ -67,7 +67,7 @@ public ArchiveExtractCallback(Func getStream, IInArchive archi /// The owner of the callback /// The list of actual indexes (solid archives support) /// The value indicating whether to preserve directory structure of extracted files. - public ArchiveExtractCallback(Func getStream, IInArchive archive, string directory, int filesCount, bool directoryStructure, + public ArchiveExtractCallback(Func getStream, IInArchive archive, string directory, int filesCount, bool directoryStructure, List actualIndexes, string password, SevenZipExtractor extractor) : base(password) { @@ -83,7 +83,7 @@ public ArchiveExtractCallback(Func getStream, IInArchive archi /// The archive files count /// The file index for the stream /// The owner of the callback - public ArchiveExtractCallback(Func getStream, IInArchive archive, Stream stream, int filesCount, uint fileIndex, + public ArchiveExtractCallback(Func getStream, IInArchive archive, Stream stream, int filesCount, uint fileIndex, SevenZipExtractor extractor) { this._getStream = getStream; @@ -99,7 +99,7 @@ public ArchiveExtractCallback(Func getStream, IInArchive archi /// The file index for the stream /// Password for the archive /// The owner of the callback - public ArchiveExtractCallback(Func getStream, IInArchive archive, Stream stream, int filesCount, uint fileIndex, string password, + public ArchiveExtractCallback(Func getStream, IInArchive archive, Stream stream, int filesCount, uint fileIndex, string password, SevenZipExtractor extractor) : base(password) { @@ -401,7 +401,7 @@ public int GetStream(uint index, out ISequentialOutStream outStream, AskMode ask } else { - outStream = _getStream(); + outStream = _getStream(_fileIndex.Value); } #endregion @@ -436,7 +436,9 @@ public int GetStream(uint index, out ISequentialOutStream outStream, AskMode ask return 0; } - public void PrepareOperation(AskMode askExtractMode) { } + public Func PrepareChecker = null; + + public void PrepareOperation(AskMode askExtractMode) { if (PrepareChecker != null) askExtractMode = PrepareChecker(askExtractMode); } /// /// Called when the archive was extracted diff --git a/SevenZip/COM.cs b/SevenZip/COM.cs index 176c44d..f73c7fc 100644 --- a/SevenZip/COM.cs +++ b/SevenZip/COM.cs @@ -381,7 +381,7 @@ public override string ToString() /// /// Stores file extraction modes. /// - internal enum AskMode + public enum AskMode { /// /// Extraction mode diff --git a/SevenZip/SevenZipExtractor.cs b/SevenZip/SevenZipExtractor.cs index e882c60..6796a76 100644 --- a/SevenZip/SevenZipExtractor.cs +++ b/SevenZip/SevenZipExtractor.cs @@ -1,5 +1,6 @@ namespace SevenZip { + using SevenZip.Sdk.Compression.Lzma; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -8,8 +9,6 @@ namespace SevenZip using System.IO; using System.Linq; - using SevenZip.Sdk.Compression.Lzma; - /// /// Class to unpack data from archives supported by 7-Zip. /// @@ -102,13 +101,13 @@ private void Init(Stream stream) if ((int)_format == -1) { _format = FileChecker.CheckSignature(stream, out _offset, out isExecutable); - } + } PreserveDirectoryStructure = true; SevenZipLibraryManager.LoadLibrary(this, _format); try { _inStream = new ArchiveEmulationStreamProxy(stream, _offset); - _packedSize = stream.Length; + _packedSize = stream.Length; _archive = SevenZipLibraryManager.InArchive(_format, this); } catch (SevenZipLibraryException) @@ -251,7 +250,7 @@ public string FileName DisposedCheck(); return _fileName; } - } + } /// /// Gets the size of the archive file @@ -316,7 +315,7 @@ public uint FilesCount GetArchiveInfo(true); } Debug.Assert(_filesCount != null); - return _filesCount.Value; + return _filesCount.Value; } } @@ -356,7 +355,7 @@ private void DisposedCheck() private ArchiveOpenCallback GetArchiveOpenCallback() { - return _openCallback ?? (_openCallback = String.IsNullOrEmpty(Password) + return _openCallback ?? (_openCallback = string.IsNullOrEmpty(Password) ? new ArchiveOpenCallback(_fileName) : new ArchiveOpenCallback(_fileName, Password)); } @@ -456,7 +455,7 @@ private void GetArchiveInfo(bool disposeStream) IInStream archiveStream; using ((archiveStream = GetArchiveStream(disposeStream)) as IDisposable) { - var openCallback = GetArchiveOpenCallback(); + ArchiveOpenCallback openCallback = GetArchiveOpenCallback(); if (!_opened) { if (!OpenArchive(archiveStream, openCallback)) @@ -469,7 +468,7 @@ private void GetArchiveInfo(bool disposeStream) _archiveFileData = new List((int)_filesCount); if (_filesCount != 0) { - var data = new PropVariant(); + PropVariant data = new PropVariant(); try { #region Getting archive items data @@ -478,7 +477,7 @@ private void GetArchiveInfo(bool disposeStream) { try { - var fileInfo = new ArchiveFileInfo { Index = (int)i }; + ArchiveFileInfo fileInfo = new ArchiveFileInfo { Index = (int)i }; _archive.GetProperty(i, ItemPropId.Path, ref data); fileInfo.FileName = NativeMethods.SafeCast(data, "[no name]"); _archive.GetProperty(i, ItemPropId.LastWriteTime, ref data); @@ -516,17 +515,17 @@ private void GetArchiveInfo(bool disposeStream) #region Getting archive properties uint numProps = _archive.GetNumberOfArchiveProperties(); - var archProps = new List((int)numProps); + List archProps = new List((int)numProps); for (uint i = 0; i < numProps; i++) { - _archive.GetArchivePropertyInfo(i, out string propName, out var propId, out var varType); + _archive.GetArchivePropertyInfo(i, out string propName, out ItemPropId propId, out ushort varType); _archive.GetArchiveProperty(propId, ref data); if (propId == ItemPropId.Solid) { _isSolid = NativeMethods.SafeCast(data, true); } - + // TODO Add more archive properties if (PropIdToName.PropIdNames.ContainsKey(propId)) { @@ -595,12 +594,12 @@ private void InitArchiveFileData(bool disposeStream) /// The array of indexes from 0 to the maximum value in the specified array private static uint[] SolidIndexes(uint[] indexes) { - int max = indexes.Aggregate(0, (current, i) => Math.Max(current, (int) i)); + int max = indexes.Aggregate(0, (current, i) => Math.Max(current, (int)i)); if (max > 0) { max++; - var res = new uint[max]; + uint[] res = new uint[max]; for (int i = 0; i < max; i++) { @@ -627,7 +626,7 @@ private void ArchiveExtractCallbackCommonInit(ArchiveExtractCallback aec) { aec.Open += ((s, e) => { _unpackedSize = (long)e.TotalSize; }); aec.FileExtractionStarted += FileExtractionStartedEventProxy; - aec.FileExtractionFinished += FileExtractionFinishedEventProxy; + aec.FileExtractionFinished += FileExtractionFinishedEventProxy; aec.Extracting += ExtractingEventProxy; aec.FileExists += FileExistsEventProxy; } @@ -642,7 +641,7 @@ private void ArchiveExtractCallbackCommonInit(ArchiveExtractCallback aec) private ArchiveExtractCallback GetArchiveExtractCallback(string directory, int filesCount, List actualIndexes) { - var aec = String.IsNullOrEmpty(Password) + ArchiveExtractCallback aec = string.IsNullOrEmpty(Password) ? new ArchiveExtractCallback(null, _archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, this) : new ArchiveExtractCallback(null, _archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, Password, this); ArchiveExtractCallbackCommonInit(aec); @@ -656,9 +655,9 @@ private ArchiveExtractCallback GetArchiveExtractCallback(string directory, int f /// The file index /// The number of files to be extracted /// The ArchiveExtractCallback callback - private ArchiveExtractCallback GetArchiveExtractCallback(Func getStream, Stream stream, uint index, int filesCount) + private ArchiveExtractCallback GetArchiveExtractCallback(Func getStream, Stream stream, uint index, int filesCount) { - var aec = String.IsNullOrEmpty(Password) + ArchiveExtractCallback aec = string.IsNullOrEmpty(Password) ? new ArchiveExtractCallback(getStream, _archive, stream, filesCount, index, this) : new ArchiveExtractCallback(getStream, _archive, stream, filesCount, index, Password, this); ArchiveExtractCallbackCommonInit(aec); @@ -682,10 +681,10 @@ private void FreeArchiveExtractCallback(ArchiveExtractCallback callback) /// The stream to check. private static void ValidateStream(Stream stream) { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } + if (stream == null) + { + throw new ArgumentNullException("stream"); + } if (!stream.CanSeek || !stream.CanRead) { throw new ArgumentException("The specified stream can not seek or read.", "stream"); @@ -717,14 +716,14 @@ private void CommonDispose() _archiveFileData = null; _archiveProperties = null; _archiveFileInfoCollection = null; - - if (_inStream != null) - { + + if (_inStream != null) + { _inStream.Dispose(); _inStream = null; - } - - if (_openCallback != null) + } + + if (_openCallback != null) { try { @@ -763,10 +762,10 @@ public void Dispose() "while making an asynchronous method call."); } if (!_disposed) - { + { CommonDispose(); } - _disposed = true; + _disposed = true; //GC.SuppressFinalize(this); } @@ -780,7 +779,7 @@ public void Dispose() /// Occurs when a new file is going to be unpacked. /// /// Occurs when 7-zip engine requests for an output stream for a new file to unpack in. - public event EventHandler FileExtractionStarted; + public event EventHandler FileExtractionStarted; /// /// Occurs when a file has been successfully unpacked. @@ -885,7 +884,7 @@ public ReadOnlyCollection ArchiveFileNames { DisposedCheck(); InitArchiveFileData(true); - var fileNames = new List(_archiveFileData.Count); + List fileNames = new List(_archiveFileData.Count); fileNames.AddRange(_archiveFileData.Select(afi => afi.FileName)); @@ -903,7 +902,7 @@ public ReadOnlyCollection VolumeFileNames DisposedCheck(); InitArchiveFileData(true); return _volumeFileNames; - } + } } #endregion @@ -917,18 +916,18 @@ public bool Check() try { InitArchiveFileData(false); - var archiveStream = GetArchiveStream(true); - var openCallback = GetArchiveOpenCallback(); + IInStream archiveStream = GetArchiveStream(true); + ArchiveOpenCallback openCallback = GetArchiveOpenCallback(); if (!OpenArchive(archiveStream, openCallback)) { return false; } - using (var aec = GetArchiveExtractCallback("", (int)_filesCount, null)) + using (ArchiveExtractCallback aec = GetArchiveExtractCallback("", (int)_filesCount, null)) { try { CheckedExecute( - _archive.Extract(null, UInt32.MaxValue, 1, aec), + _archive.Extract(null, uint.MaxValue, 1, aec), SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec); } finally @@ -988,6 +987,60 @@ public void ExtractFile(string fileName, Stream stream) } } + /// + /// Unpacks the file by its index to the specified stream. + /// + /// Index in the archive file table. + /// The stream where the file is to be unpacked. + public void ExtractFileSolid(int index, Stream stream) + { + //DisposedCheck(); + //ClearExceptions(); + //if (!CheckIndexes(index)) + //{ + // if (!ThrowException(null, new ArgumentException("The index must be more or equal to zero.", "index"))) + // { + // return; + // } + //} + //if (!stream.CanWrite) + //{ + // if (!ThrowException(null, new ArgumentException("The specified stream can not be written.", "stream"))) + // { + // return; + // } + //} + //if (_archiveFileData == null) + //{ + // InitArchiveFileData(false); + //} + + //if (index > _filesCount - 1) + //{ + // if (!ThrowException(null, new ArgumentOutOfRangeException( + // "index", "The specified index is greater than the archive files count."))) + // { + // return; + // } + //} + + //var indexes = ArchiveFileData.Select(k => (uint)k.Index).ToArray(); + OutStreamWrapper osw = null; + Func getStream = (ind) => + { + if (ind == index) + { + osw = new OutStreamWrapper(stream, false); + return osw; + } + else + { + return null; + } + }; + ExtractFilesSolidIndividual(null, getStream, null, null, null, index); + } + /// /// Unpacks the file by its index to the specified stream. /// @@ -1020,25 +1073,28 @@ public void ExtractFile(int index, Stream stream) return; } } - var indexes = new[] {(uint) index}; + uint[] indexes = new[] { (uint)index }; if (_isSolid.Value) { + //_archiveStream = null; + ExtractFileSolid(index, stream); + return; indexes = SolidIndexes(indexes); } - var archiveStream = GetArchiveStream(false); - var openCallback = GetArchiveOpenCallback(); + IInStream archiveStream = GetArchiveStream(false); + ArchiveOpenCallback openCallback = GetArchiveOpenCallback(); if (!OpenArchive(archiveStream, openCallback)) { return; } try { - using (var aec = GetArchiveExtractCallback(null, stream, (uint) index, indexes.Length)) + using (ArchiveExtractCallback aec = GetArchiveExtractCallback(null, stream, (uint)index, indexes.Length)) { try { CheckedExecute( - _archive.Extract(indexes, (uint) indexes.Length, 0, aec), + _archive.Extract(indexes, (uint)indexes.Length, 0, aec), SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec); } finally @@ -1081,23 +1137,23 @@ public void ExtractFiles(string directory, params int[] indexes) #region Indexes stuff - var uindexes = new uint[indexes.Length]; + uint[] uindexes = new uint[indexes.Length]; for (int i = 0; i < indexes.Length; i++) { - uindexes[i] = (uint) indexes[i]; + uindexes[i] = (uint)indexes[i]; } if (uindexes.Where(i => i >= _filesCount).Any( - i => !ThrowException(null, - new ArgumentOutOfRangeException("indexes", - "Index must be less than " + + i => !ThrowException(null, + new ArgumentOutOfRangeException("indexes", + "Index must be less than " + _filesCount.Value.ToString( CultureInfo.InvariantCulture) + "!")))) { return; } - var origIndexes = new List(uindexes); + List origIndexes = new List(uindexes); origIndexes.Sort(); uindexes = origIndexes.ToArray(); if (_isSolid.Value) @@ -1112,19 +1168,19 @@ public void ExtractFiles(string directory, params int[] indexes) IInStream archiveStream; using ((archiveStream = GetArchiveStream(origIndexes.Count != 1)) as IDisposable) { - var openCallback = GetArchiveOpenCallback(); + ArchiveOpenCallback openCallback = GetArchiveOpenCallback(); if (!OpenArchive(archiveStream, openCallback)) { return; } try { - using (var aec = GetArchiveExtractCallback(directory, (int) _filesCount, origIndexes)) + using (ArchiveExtractCallback aec = GetArchiveExtractCallback(directory, (int)_filesCount, origIndexes)) { try { CheckedExecute( - _archive.Extract(uindexes, (uint) uindexes.Length, 0, aec), + _archive.Extract(uindexes, (uint)uindexes.Length, 0, aec), SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec); } finally @@ -1167,8 +1223,8 @@ public void ExtractFiles(string directory, params string[] fileNames) { DisposedCheck(); InitArchiveFileData(false); - var indexes = new List(fileNames.Length); - var archiveFileNames = new List(ArchiveFileNames); + List indexes = new List(fileNames.Length); + List archiveFileNames = new List(ArchiveFileNames); foreach (string fn in fileNames) { if (!archiveFileNames.Contains(fn)) @@ -1196,6 +1252,65 @@ public void ExtractFiles(string directory, params string[] fileNames) } ExtractFiles(directory, indexes.ToArray()); } + /// + /// Extracts files from the archive, giving a callback the choice what + /// to do with each file. The order of the files is given by the archive. + /// 7-Zip (and any other solid) archives are NOT supported. + /// + /// The callback to call for each file in the archive. + public void ExtractFilesSolidIndividual(ExtractFileCallback extractFileCallback, Func getStream = null, Action FileExtractStart = null, Action FileExtractComplete = null, Func prepareCheck = null, int? entryIndex = null) + { + //DisposedCheck(); + //InitArchiveFileData(false); + if (IsSolid && getStream != null) + { + DisposedCheck(); + ClearExceptions(); + ArchiveOpenCallback openCallback = null; + + if (!_opened) + { + IInStream archiveStream = GetArchiveStream(false); + openCallback = GetArchiveOpenCallback(); + if (!OpenArchive(archiveStream, openCallback)) + { + return; + } + } + try + { + ArchiveExtractCallback aec = null; + + aec = GetArchiveExtractCallback(getStream, null, (uint)entryIndex.Value, 1); + + using (aec) + { + + try + { + + CheckedExecute( + _archive.Extract(new uint[] { (uint)entryIndex.Value }, 1, 0, aec), + SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec); + } + finally + { + aec.PrepareChecker = null; + FreeArchiveExtractCallback(aec); + } + } + } + catch (Exception) + { + if (openCallback != null && openCallback.ThrowException()) + { + throw; + } + } + OnEvent(ExtractionFinished, EventArgs.Empty, false); + ThrowUserException(); + } + } /// /// Extracts files from the archive, giving a callback the choice what @@ -1203,7 +1318,7 @@ public void ExtractFiles(string directory, params string[] fileNames) /// 7-Zip (and any other solid) archives are NOT supported. /// /// The callback to call for each file in the archive. - public void ExtractFiles(ExtractFileCallback extractFileCallback, Func getStream = null, Action FileExtractStart = null, Action FileExtractComplete = null) + public void ExtractFiles(ExtractFileCallback extractFileCallback, Func getStream = null, Action FileExtractStart = null, Action FileExtractComplete = null, Func prepareCheck = null, int? entryIndex = null) { DisposedCheck(); InitArchiveFileData(false); @@ -1212,27 +1327,54 @@ public void ExtractFiles(ExtractFileCallback extractFileCallback, Func k.Index)).Select(k=>(uint)k).ToArray(); - var indexes = ArchiveFileData.Select(k => (uint)k.Index).ToArray(); - var archiveStream = GetArchiveStream(false); - var openCallback = GetArchiveOpenCallback(); + uint[] indexes = ArchiveFileData.Select(k => (uint)k.Index).ToArray(); + IInStream archiveStream = GetArchiveStream(false); + ArchiveOpenCallback openCallback = GetArchiveOpenCallback(); if (!OpenArchive(archiveStream, openCallback)) { return; } try { - using (var aec = GetArchiveExtractCallback(getStream, null, (uint)0, indexes.Length)) + ArchiveExtractCallback aec = null; + if (entryIndex != null) + { + aec = GetArchiveExtractCallback(getStream, null, (uint)entryIndex.Value, 1); + } + else + { + aec = GetArchiveExtractCallback(getStream, null, 0, indexes.Length); + } + + using (aec) { - aec.FileExtractionStarted += (src, fi) => { FileExtractStart?.Invoke(fi); }; - aec.FileExtractionFinished += (src, fi) => { FileExtractComplete?.Invoke(fi); }; + aec.PrepareChecker = prepareCheck; + Action actionStart = new Action((src, fi) => { FileExtractStart?.Invoke(fi); }); + Action actionFinished = new Action((src, fi) => { FileExtractComplete?.Invoke(fi); }); + EventHandler deleStart = new EventHandler(actionStart); + EventHandler deleFinished = new EventHandler(actionFinished); + aec.FileExtractionStarted += deleStart; + aec.FileExtractionFinished += deleFinished; try { - CheckedExecute( + if (entryIndex != null) + { + CheckedExecute( + _archive.Extract(new uint[] { (uint)entryIndex.Value }, 1, 0, aec), + SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec); + } + else + { + CheckedExecute( _archive.Extract(indexes, (uint)indexes.Length, 0, aec), SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec); + } } finally { + aec.PrepareChecker = null; + aec.FileExtractionStarted -= deleStart; + aec.FileExtractionFinished -= deleFinished; FreeArchiveExtractCallback(aec); } } @@ -1251,7 +1393,7 @@ public void ExtractFiles(ExtractFileCallback extractFileCallback, FuncThe directory where the files are to be unpacked. public void ExtractArchive(string directory) { - DisposedCheck(); + DisposedCheck(); ClearExceptions(); InitArchiveFileData(false); try @@ -1311,19 +1453,19 @@ public void ExtractArchive(string directory) IInStream archiveStream; using ((archiveStream = GetArchiveStream(true)) as IDisposable) { - var openCallback = GetArchiveOpenCallback(); + ArchiveOpenCallback openCallback = GetArchiveOpenCallback(); if (!OpenArchive(archiveStream, openCallback)) { return; } try { - using (var aec = GetArchiveExtractCallback(directory, (int) _filesCount, null)) + using (ArchiveExtractCallback aec = GetArchiveExtractCallback(directory, (int)_filesCount, null)) { try { CheckedExecute( - _archive.Extract(null, UInt32.MaxValue, 0, aec), + _archive.Extract(null, uint.MaxValue, 0, aec), SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec); OnEvent(ExtractionFinished, EventArgs.Empty, false); } @@ -1352,7 +1494,7 @@ public void ExtractArchive(string directory) _opened = false; } ThrowUserException(); - } + } #endregion #endif @@ -1361,7 +1503,7 @@ public void ExtractArchive(string directory) internal static byte[] GetLzmaProperties(Stream inStream, out long outSize) { - var lzmAproperties = new byte[5]; + byte[] lzmAproperties = new byte[5]; if (inStream.Read(lzmAproperties, 0, 5) != 5) { throw new LzmaException(); @@ -1374,7 +1516,7 @@ internal static byte[] GetLzmaProperties(Stream inStream, out long outSize) { throw new LzmaException(); } - outSize |= ((long) (byte) b) << (i << 3); + outSize |= ((long)(byte)b) << (i << 3); } return lzmAproperties; } @@ -1393,9 +1535,9 @@ public static void DecompressStream(Stream inStream, Stream outStream, int? inLe { throw new ArgumentException("The specified streams are invalid."); } - var decoder = new Decoder(); - var inSize = (inLength ?? inStream.Length) - inStream.Position; - decoder.SetDecoderProperties(GetLzmaProperties(inStream, out var outSize)); + Decoder decoder = new Decoder(); + long inSize = (inLength ?? inStream.Length) - inStream.Position; + decoder.SetDecoderProperties(GetLzmaProperties(inStream, out long outSize)); decoder.Code( inStream, outStream, inSize, outSize, new LzmaProgressCallback(inSize, codeProgressEvent)); @@ -1408,14 +1550,14 @@ public static void DecompressStream(Stream inStream, Stream outStream, int? inLe /// Decompressed byte array public static byte[] ExtractBytes(byte[] data) { - using (var inStream = new MemoryStream(data)) + using (MemoryStream inStream = new MemoryStream(data)) { - var decoder = new Decoder(); + Decoder decoder = new Decoder(); inStream.Seek(0, 0); - using (var outStream = new MemoryStream()) + using (MemoryStream outStream = new MemoryStream()) { - decoder.SetDecoderProperties(GetLzmaProperties(inStream, out var outSize)); + decoder.SetDecoderProperties(GetLzmaProperties(inStream, out long outSize)); decoder.Code(inStream, outStream, inStream.Length - inStream.Position, outSize, null); return outStream.ToArray(); } diff --git a/SevenZip/SevenZipExtractorAsynchronous.cs b/SevenZip/SevenZipExtractorAsynchronous.cs index bd79d79..71d25cc 100644 --- a/SevenZip/SevenZipExtractorAsynchronous.cs +++ b/SevenZip/SevenZipExtractorAsynchronous.cs @@ -93,7 +93,7 @@ internal override void ReleaseContext() /// /// The callback to call for each file in the archive. private delegate void ExtractFiles3Delegate(ExtractFileCallback extractFileCallback); - private delegate void ExtractFiles4Delegate(ExtractFileCallback extractFileCallback, Func getStream, Action FileExtractStart, Action FileExtractComplete); + private delegate void ExtractFiles4Delegate(ExtractFileCallback extractFileCallback, Func getStream, Action FileExtractStart, Action FileExtractComplete, Func prepareCheck, int? entryIndex); #endregion /// @@ -156,10 +156,10 @@ public void BeginExtractFiles(string directory, params string[] fileNames) /// 7-Zip (and any other solid) archives are NOT supported. /// /// The callback to call for each file in the archive. - public void BeginExtractFiles(ExtractFileCallback extractFileCallback, Func getStream = null, Action FileExtractStart = null, Action FileExtractComplete = null) + public void BeginExtractFiles(ExtractFileCallback extractFileCallback, Func getStream = null, Action FileExtractStart = null, Action FileExtractComplete = null, Func prepareCheck = null, int? entryIndex = null) { SaveContext(); - new ExtractFiles4Delegate(ExtractFiles).BeginInvoke(extractFileCallback, getStream, FileExtractStart, FileExtractComplete, AsyncCallbackImplementation, this); + new ExtractFiles4Delegate(ExtractFiles).BeginInvoke(extractFileCallback, getStream, FileExtractStart, FileExtractComplete, prepareCheck, entryIndex, AsyncCallbackImplementation, this); } } }