mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-10-01 11:22:44 +00:00
fix deflate rewinding?
This commit is contained in:
@@ -2,15 +2,12 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
internal sealed partial class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
internal partial class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
{
|
||||
private IEnumerable<ZipEntry>? _entries;
|
||||
|
||||
@@ -134,13 +131,8 @@ internal sealed partial class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
{
|
||||
_lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize;
|
||||
|
||||
// For SeekableRewindableStream, seek back to just after the local header signature.
|
||||
// Plain RewindableStream cannot seek to arbitrary positions, so we skip this.
|
||||
// 4 = First 4 bytes of the entry header (i.e. 50 4B 03 04)
|
||||
if (rewindableStream is SeekableRewindableStream)
|
||||
{
|
||||
rewindableStream.Position = pos.Value + 4;
|
||||
}
|
||||
rewindableStream.Position = pos.Value + 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -181,38 +173,14 @@ internal sealed partial class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
} // Check if zip is streaming ( Length is 0 and is declared in PostDataDescriptor )
|
||||
else if (local_header.Flags.HasFlag(HeaderFlags.UsePostDataDescriptor))
|
||||
{
|
||||
// Peek ahead to check if next data is a header or file data.
|
||||
// For SeekableRewindableStream, use direct position save/restore to avoid
|
||||
// interfering with any recording state set by the caller (e.g., ReaderFactory).
|
||||
// Plain RewindableStream can use StartRecording/Rewind safely since it was
|
||||
// created fresh by EnsureSeekable and isn't shared with the caller.
|
||||
if (rewindableStream is SeekableRewindableStream)
|
||||
var nextHeaderBytes = reader.ReadUInt32();
|
||||
if (rewindableStream is IStreamStack ss)
|
||||
{
|
||||
var savedPosition = rewindableStream.Position;
|
||||
var nextHeaderBytes = reader.ReadUInt32();
|
||||
rewindableStream.Position = savedPosition;
|
||||
header.HasData = !IsHeader(nextHeaderBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only start recording if not already recording.
|
||||
// The stream may already be recording if it was created by ReaderFactory.
|
||||
if (!rewindableStream.IsRecording)
|
||||
{
|
||||
rewindableStream.StartRecording();
|
||||
var nextHeaderBytes = reader.ReadUInt32();
|
||||
rewindableStream.Rewind(true);
|
||||
header.HasData = !IsHeader(nextHeaderBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If already recording, save position and restore after peek
|
||||
var savedPosition = rewindableStream.Position;
|
||||
var nextHeaderBytes = reader.ReadUInt32();
|
||||
rewindableStream.Position = savedPosition;
|
||||
header.HasData = !IsHeader(nextHeaderBytes);
|
||||
}
|
||||
ss.Rewind(sizeof(uint));
|
||||
}
|
||||
|
||||
// Check if next data is PostDataDescriptor, streamed file with 0 length
|
||||
header.HasData = !IsHeader(nextHeaderBytes);
|
||||
}
|
||||
else // We are not streaming and compressed size is 0, we have no data
|
||||
{
|
||||
|
||||
@@ -64,10 +64,6 @@ public partial class DeflateStream : Stream, IStreamStack
|
||||
leaveOpen,
|
||||
forceEncoding
|
||||
);
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(DeflateStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
#region Zlib properties
|
||||
|
||||
@@ -593,25 +593,16 @@ internal class ZlibBaseStream : Stream, IStreamStack
|
||||
{
|
||||
_stream.Flush();
|
||||
}
|
||||
else if (_streamMode == StreamMode.Reader && z.AvailableBytesIn > 0)
|
||||
else if (z.AvailableBytesIn > 0)
|
||||
{
|
||||
// Rewind the underlying stream by the number of unconsumed bytes in the buffer
|
||||
// This handles the case where the decompressor over-read past the end of the entry
|
||||
if (_stream is IStreamStack stack)
|
||||
{
|
||||
var root = stack.GetRootStream();
|
||||
if (root.CanSeek)
|
||||
{
|
||||
// Try to seek backward on the root stream; ignore if not supported
|
||||
try
|
||||
{
|
||||
root.Seek(-z.AvailableBytesIn, SeekOrigin.Current);
|
||||
}
|
||||
catch (NotSupportedException) { }
|
||||
}
|
||||
stack.Rewind(z.AvailableBytesIn);
|
||||
}
|
||||
z.AvailableBytesIn = 0;
|
||||
}
|
||||
z.AvailableBytesIn = 0;
|
||||
}
|
||||
|
||||
public override async Task FlushAsync(CancellationToken cancellationToken)
|
||||
@@ -623,25 +614,16 @@ internal class ZlibBaseStream : Stream, IStreamStack
|
||||
{
|
||||
await _stream.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else if (_streamMode == StreamMode.Reader && z.AvailableBytesIn > 0)
|
||||
else if (z.AvailableBytesIn > 0)
|
||||
{
|
||||
// Rewind the underlying stream by the number of unconsumed bytes in the buffer
|
||||
// This handles the case where the decompressor over-read past the end of the entry
|
||||
if (_stream is IStreamStack stack)
|
||||
{
|
||||
var root = stack.GetRootStream();
|
||||
if (root.CanSeek)
|
||||
{
|
||||
// Try to seek backward on the root stream; ignore if not supported
|
||||
try
|
||||
{
|
||||
root.Seek(-z.AvailableBytesIn, SeekOrigin.Current);
|
||||
}
|
||||
catch (NotSupportedException) { }
|
||||
}
|
||||
stack.Rewind(z.AvailableBytesIn);
|
||||
}
|
||||
z.AvailableBytesIn = 0;
|
||||
}
|
||||
z.AvailableBytesIn = 0;
|
||||
}
|
||||
|
||||
public override Int64 Seek(Int64 offset, SeekOrigin origin) =>
|
||||
@@ -1016,7 +998,7 @@ internal class ZlibBaseStream : Stream, IStreamStack
|
||||
if (rc == ZlibConstants.Z_STREAM_END && z.AvailableBytesIn != 0 && !_wantCompress)
|
||||
{
|
||||
//rewind the buffer
|
||||
//unused: ((IStreamStack)this).Rewind(z.AvailableBytesIn);
|
||||
this.Rewind(z.AvailableBytesIn);
|
||||
z.AvailableBytesIn = 0;
|
||||
}
|
||||
|
||||
@@ -1208,7 +1190,7 @@ internal class ZlibBaseStream : Stream, IStreamStack
|
||||
if (rc == ZlibConstants.Z_STREAM_END && z.AvailableBytesIn != 0 && !_wantCompress)
|
||||
{
|
||||
//rewind the buffer
|
||||
//unused: ((IStreamStack)this).Rewind(z.AvailableBytesIn);
|
||||
this.Rewind(z.AvailableBytesIn);
|
||||
z.AvailableBytesIn = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,6 @@ namespace SharpCompress.IO;
|
||||
|
||||
internal partial class BufferedSubStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
private readonly Stream _stream;
|
||||
@@ -19,18 +15,12 @@ internal partial class BufferedSubStream : Stream, IStreamStack
|
||||
public BufferedSubStream(Stream stream, long origin, long bytesToRead)
|
||||
{
|
||||
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(BufferedSubStream));
|
||||
#endif
|
||||
this.origin = origin;
|
||||
this.BytesLeftToRead = bytesToRead;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(BufferedSubStream));
|
||||
#endif
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -49,5 +49,22 @@ namespace SharpCompress.IO
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
internal static void Rewind(this IStreamStack stream, int count)
|
||||
{
|
||||
IStreamStack? buffStream = null;
|
||||
IStreamStack? current = stream;
|
||||
|
||||
while (buffStream == null && current != null)
|
||||
{
|
||||
if (current is RewindableStream rewindableStream)
|
||||
{
|
||||
buffStream = current;
|
||||
rewindableStream.Position -= Math.Min(rewindableStream.Position, count);
|
||||
}
|
||||
current = current.BaseStream() as IStreamStack;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ using System.IO;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
internal partial class RewindableStream : Stream
|
||||
internal partial class RewindableStream : Stream, IStreamStack
|
||||
{
|
||||
public virtual Stream BaseStream() => stream;
|
||||
|
||||
private readonly Stream stream;
|
||||
private MemoryStream bufferStream = new MemoryStream();
|
||||
private bool isRewound;
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace SharpCompress.IO;
|
||||
|
||||
internal sealed partial class SeekableRewindableStream : RewindableStream
|
||||
{
|
||||
public override Stream BaseStream() => _underlyingStream;
|
||||
private readonly Stream _underlyingStream;
|
||||
private long? _recordedPosition;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user