reintroduce RewindableStream stream. SharpCompressStream does too much
This commit is contained in:
parent
24b4ef8780
commit
c770bc4788
8 changed files with 208 additions and 39 deletions
|
|
@ -35,8 +35,14 @@ internal partial class ZStandardStream : DecompressionStream, IStreamStack
|
|||
|
||||
internal static bool IsZStandard(Stream stream)
|
||||
{
|
||||
var br = new BinaryReader(stream);
|
||||
var magic = br.ReadUInt32();
|
||||
var buffer = new byte[4];
|
||||
var bytesRead = stream.Read(buffer, 0, 4);
|
||||
if (bytesRead < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var magic = BitConverter.ToUInt32(buffer, 0);
|
||||
if (ZstandardConstants.MAGIC != magic)
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -36,8 +36,11 @@ namespace SharpCompress.Factories
|
|||
var buffer = ArrayPool<byte>.Shared.Rent(2);
|
||||
try
|
||||
{
|
||||
stream.ReadExact(buffer, 0, 2);
|
||||
return buffer[0] == 0x1A && buffer[1] < 10; //rather thin, but this is all we have
|
||||
if (stream.ReadFully(buffer.AsSpan(0, 2)))
|
||||
{
|
||||
return buffer[0] == 0x1A && buffer[1] < 10; //rather thin, but this is all we have
|
||||
}
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public abstract class Factory : IFactory
|
|||
/// <param name="reader"></param>
|
||||
/// <returns></returns>
|
||||
internal virtual bool TryOpenReader(
|
||||
SharpCompressStream stream,
|
||||
RewindableStream stream,
|
||||
ReaderOptions options,
|
||||
out IReader? reader
|
||||
)
|
||||
|
|
@ -84,16 +84,15 @@ public abstract class Factory : IFactory
|
|||
|
||||
if (this is IReaderFactory readerFactory)
|
||||
{
|
||||
long pos = ((IStreamStack)stream).GetPosition();
|
||||
|
||||
stream.Rewind();
|
||||
if (IsArchive(stream, options.Password))
|
||||
{
|
||||
((IStreamStack)stream).StackSeek(pos);
|
||||
stream.Rewind();
|
||||
reader = readerFactory.OpenReader(stream, options);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
stream.Rewind();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,31 +107,28 @@ public class GZipFactory
|
|||
|
||||
/// <inheritdoc/>
|
||||
internal override bool TryOpenReader(
|
||||
SharpCompressStream rewindableStream,
|
||||
RewindableStream rewindableStream,
|
||||
ReaderOptions options,
|
||||
out IReader? reader
|
||||
)
|
||||
{
|
||||
reader = null;
|
||||
|
||||
long pos = ((IStreamStack)rewindableStream).GetPosition();
|
||||
|
||||
if (GZipArchive.IsGZipFile(rewindableStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
var testStream = new GZipStream(rewindableStream, CompressionMode.Decompress);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
reader = new TarReader(rewindableStream, options, CompressionType.GZip);
|
||||
return true;
|
||||
}
|
||||
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
reader = OpenReader(rewindableStream, options);
|
||||
return true;
|
||||
}
|
||||
|
||||
rewindableStream.Rewind();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
|
|||
#region reader
|
||||
|
||||
internal override bool TryOpenReader(
|
||||
SharpCompressStream rewindableStream,
|
||||
RewindableStream rewindableStream,
|
||||
ReaderOptions options,
|
||||
out IReader? reader
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -47,18 +49,18 @@ public class TarFactory
|
|||
/// <inheritdoc/>
|
||||
public override bool IsArchive(Stream stream, string? password = null)
|
||||
{
|
||||
var rewindableStream = new SharpCompressStream(stream);
|
||||
long pos = rewindableStream.GetPosition();
|
||||
var rewindableStream = new RewindableStream(stream);
|
||||
rewindableStream.StartRecording();
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
if (wrapper.IsMatch(rewindableStream))
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
var decompressedStream = wrapper.CreateStream(rewindableStream);
|
||||
if (TarArchive.IsTarFile(decompressedStream))
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -74,18 +76,18 @@ public class TarFactory
|
|||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var rewindableStream = new SharpCompressStream(stream);
|
||||
long pos = rewindableStream.GetPosition();
|
||||
var rewindableStream = new RewindableStream(stream);
|
||||
rewindableStream.StartRecording();
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
if (await wrapper.IsMatchAsync(rewindableStream, cancellationToken))
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
var decompressedStream = wrapper.CreateStream(rewindableStream);
|
||||
if (await TarArchive.IsTarFileAsync(decompressedStream, cancellationToken))
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -155,18 +157,18 @@ public class TarFactory
|
|||
public IReader OpenReader(Stream stream, ReaderOptions? options)
|
||||
{
|
||||
options ??= new ReaderOptions();
|
||||
var rewindableStream = new SharpCompressStream(stream);
|
||||
long pos = rewindableStream.GetPosition();
|
||||
var rewindableStream = new RewindableStream(stream);
|
||||
rewindableStream.StartRecording();
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
if (wrapper.IsMatch(rewindableStream))
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
var decompressedStream = wrapper.CreateStream(rewindableStream);
|
||||
if (TarArchive.IsTarFile(decompressedStream))
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
rewindableStream.Rewind();
|
||||
return new TarReader(rewindableStream, options, wrapper.CompressionType);
|
||||
}
|
||||
}
|
||||
|
|
@ -184,7 +186,7 @@ public class TarFactory
|
|||
cancellationToken.ThrowIfCancellationRequested();
|
||||
options ??= new ReaderOptions();
|
||||
var rewindableStream = new SharpCompressStream(stream);
|
||||
long pos = rewindableStream.GetPosition();
|
||||
var pos = rewindableStream.GetPosition();
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
{
|
||||
rewindableStream.StackSeek(pos);
|
||||
|
|
|
|||
163
src/SharpCompress/IO/RewindableStream.cs
Normal file
163
src/SharpCompress/IO/RewindableStream.cs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.IO
|
||||
{
|
||||
internal partial class RewindableStream : Stream
|
||||
{
|
||||
private readonly Stream stream;
|
||||
private MemoryStream bufferStream = new MemoryStream();
|
||||
private bool isRewound;
|
||||
private bool isDisposed;
|
||||
|
||||
public RewindableStream(Stream stream)
|
||||
{
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
internal bool IsRecording { get; private set; }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
isDisposed = true;
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Rewind(bool stopRecording = false)
|
||||
{
|
||||
isRewound = true;
|
||||
IsRecording = !stopRecording;
|
||||
bufferStream.Position = 0;
|
||||
}
|
||||
|
||||
public void Rewind(MemoryStream buffer)
|
||||
{
|
||||
if (bufferStream.Position >= buffer.Length)
|
||||
{
|
||||
bufferStream.Position -= buffer.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
bufferStream.TransferTo(buffer, buffer.Length - bufferStream.Position);
|
||||
//create new memorystream to allow proper resizing as memorystream could be a user provided buffer
|
||||
//https://github.com/adamhathcock/sharpcompress/issues/306
|
||||
bufferStream = new MemoryStream();
|
||||
buffer.Position = 0;
|
||||
buffer.TransferTo(bufferStream, buffer.Length);
|
||||
bufferStream.Position = 0;
|
||||
}
|
||||
isRewound = true;
|
||||
}
|
||||
|
||||
public void StartRecording()
|
||||
{
|
||||
//if (isRewound && bufferStream.Position != 0)
|
||||
// throw new System.NotImplementedException();
|
||||
if (bufferStream.Position != 0)
|
||||
{
|
||||
byte[] data = bufferStream.ToArray();
|
||||
long position = bufferStream.Position;
|
||||
bufferStream.SetLength(0);
|
||||
bufferStream.Write(data, (int)position, data.Length - (int)position);
|
||||
bufferStream.Position = 0;
|
||||
}
|
||||
IsRecording = true;
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => stream.CanSeek;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Length => throw new NotSupportedException();
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return stream.Position + bufferStream.Position - bufferStream.Length; }
|
||||
set
|
||||
{
|
||||
if (!isRewound)
|
||||
{
|
||||
stream.Position = value;
|
||||
}
|
||||
else if (value < stream.Position - bufferStream.Length || value >= stream.Position)
|
||||
{
|
||||
stream.Position = value;
|
||||
isRewound = false;
|
||||
bufferStream.SetLength(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferStream.Position = value - stream.Position + bufferStream.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
//don't actually read if we don't really want to read anything
|
||||
//currently a network stream bug on Windows for .NET Core
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int read;
|
||||
if (isRewound && bufferStream.Position != bufferStream.Length)
|
||||
{
|
||||
read = bufferStream.Read(buffer, offset, count);
|
||||
if (read < count)
|
||||
{
|
||||
int tempRead = stream.Read(buffer, offset + read, count - read);
|
||||
if (IsRecording)
|
||||
{
|
||||
bufferStream.Write(buffer, offset + read, tempRead);
|
||||
}
|
||||
read += tempRead;
|
||||
}
|
||||
if (bufferStream.Position == bufferStream.Length && !IsRecording)
|
||||
{
|
||||
isRewound = false;
|
||||
bufferStream.SetLength(0);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
read = stream.Read(buffer, offset, count);
|
||||
if (IsRecording)
|
||||
{
|
||||
bufferStream.Write(buffer, offset, read);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,9 +34,8 @@ public static partial class ReaderFactory
|
|||
stream.NotNull(nameof(stream));
|
||||
options ??= new ReaderOptions() { LeaveStreamOpen = false };
|
||||
|
||||
var bStream = new SharpCompressStream(stream, bufferSize: options.BufferSize);
|
||||
|
||||
long pos = bStream.GetPosition();
|
||||
var bStream = new RewindableStream(stream);
|
||||
bStream.StartRecording();
|
||||
|
||||
var factories = Factories.Factory.Factories.OfType<Factories.Factory>();
|
||||
|
||||
|
|
@ -53,9 +52,9 @@ public static partial class ReaderFactory
|
|||
&& reader != null
|
||||
)
|
||||
{
|
||||
bStream.Rewind();
|
||||
return reader;
|
||||
}
|
||||
bStream.StackSeek(pos);
|
||||
}
|
||||
|
||||
foreach (var factory in factories)
|
||||
|
|
@ -64,7 +63,7 @@ public static partial class ReaderFactory
|
|||
{
|
||||
continue; // Already tested above
|
||||
}
|
||||
bStream.StackSeek(pos);
|
||||
bStream.Rewind();
|
||||
if (factory.TryOpenReader(bStream, options, out var reader) && reader != null)
|
||||
{
|
||||
return reader;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue