From afff386622402efcd815184f46010e19502c82d8 Mon Sep 17 00:00:00 2001 From: Paul Newman Date: Wed, 15 Jul 2015 13:44:20 +0100 Subject: [PATCH] Skip entry stream on dispose Until now the caller had to completely consume each entry stream, or call SkipEntry(), before disposing the stream. If not, exception was thrown: "EntryStream has not been fully consumed". Hugely inconvenient; a user-thrown exception inside a "using (EntryStream)" block would be discarded. Now automatically skips the entry on dispose. Added method EntryStream.Cancel(). Call this if entry stream is unfinished, and no further entries are required. Helps with efficiency, as it avoids reading data that is not needed. --- .../Archive/SevenZip/SevenZipArchive.cs | 2 +- SharpCompress/Common/EntryStream.cs | 21 ++++++++++++++++--- SharpCompress/Reader/AbstractReader.cs | 18 +++++++++++++++- SharpCompress/Reader/Rar/RarReader.cs | 2 +- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/SharpCompress/Archive/SevenZip/SevenZipArchive.cs b/SharpCompress/Archive/SevenZip/SevenZipArchive.cs index 14792f67..3af441e0 100644 --- a/SharpCompress/Archive/SevenZip/SevenZipArchive.cs +++ b/SharpCompress/Archive/SevenZip/SevenZipArchive.cs @@ -236,7 +236,7 @@ namespace SharpCompress.Archive.SevenZip protected override EntryStream GetEntryStream() { - return new EntryStream(new ReadOnlySubStream(currentStream, currentItem.Size)); + return CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size)); } } } diff --git a/SharpCompress/Common/EntryStream.cs b/SharpCompress/Common/EntryStream.cs index 606bb160..a14bbbbb 100644 --- a/SharpCompress/Common/EntryStream.cs +++ b/SharpCompress/Common/EntryStream.cs @@ -26,12 +26,27 @@ namespace SharpCompress.Common completed = true; } + public bool Cancelled { get; private set; } + + /// + /// Indicates that the remainder of the stream is not required. + /// On dispose, the entry will not be skipped, so it helps with efficiency. + /// The downside is that subsequent entries are not usable, as the compressed stream is not positioned at an entry boundary. + /// + public void Cancel() + { + if (!completed) + { + Cancelled = true; + stream.Close(); + } + } + protected override void Dispose(bool disposing) { - if (!completed) + if (!(completed || Cancelled)) { - throw new InvalidOperationException( - "EntryStream has not been fully consumed. Read the entire stream or use SkipEntry."); + SkipEntry(); } if (isDisposed) { diff --git a/SharpCompress/Reader/AbstractReader.cs b/SharpCompress/Reader/AbstractReader.cs index 0c98e032..bd940ce8 100644 --- a/SharpCompress/Reader/AbstractReader.cs +++ b/SharpCompress/Reader/AbstractReader.cs @@ -77,6 +77,12 @@ namespace SharpCompress.Reader { return LoadStreamForReading(RequestInitialStream()); } + + if (currentEntryStream != null && currentEntryStream.Cancelled) + { + throw new InvalidOperationException("EntryStream has not been fully consumed. Read the entire stream or use SkipEntry."); + } + if (!wroteCurrentEntry) { SkipEntry(); @@ -197,9 +203,19 @@ namespace SharpCompress.Reader return stream; } + private EntryStream currentEntryStream; + + /// + /// Retains a reference to the entry stream, so we can check whether it completed later. + /// + protected EntryStream CreateEntryStream(Stream decompressed) + { + return currentEntryStream = new EntryStream(decompressed); + } + protected virtual EntryStream GetEntryStream() { - return new EntryStream(Entry.Parts.First().GetCompressedStream()); + return CreateEntryStream(Entry.Parts.First().GetCompressedStream()); } #endregion diff --git a/SharpCompress/Reader/Rar/RarReader.cs b/SharpCompress/Reader/Rar/RarReader.cs index 68ee8b2f..58a1be35 100644 --- a/SharpCompress/Reader/Rar/RarReader.cs +++ b/SharpCompress/Reader/Rar/RarReader.cs @@ -73,7 +73,7 @@ namespace SharpCompress.Reader.Rar protected override EntryStream GetEntryStream() { - return new EntryStream(new RarStream(pack, Entry.FileHeader, + return CreateEntryStream(new RarStream(pack, Entry.FileHeader, new MultiVolumeReadOnlyStream( CreateFilePartEnumerableForCurrentEntry().Cast(), this))); }