mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-24 16:05:27 +00:00
Started rar async...interface changes were viral
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.GZip;
|
||||
|
||||
namespace SharpCompress.Archives.GZip;
|
||||
@@ -20,6 +21,8 @@ public class GZipArchiveEntry : GZipEntry, IArchiveEntry
|
||||
return Parts.Single().GetCompressedStream().NotNull();
|
||||
}
|
||||
|
||||
public virtual async Task<Stream> OpenEntryStreamAsync() => await Task.FromResult(OpenEntryStream());
|
||||
|
||||
#region IArchiveEntry Members
|
||||
|
||||
public IArchive Archive { get; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
@@ -11,6 +12,11 @@ public interface IArchiveEntry : IEntry
|
||||
/// </summary>
|
||||
Stream OpenEntryStream();
|
||||
|
||||
/// <summary>
|
||||
/// Opens the current entry as a stream that will decompress as it is read.
|
||||
/// Read the entire stream or use SkipEntry on EntryStream.
|
||||
/// </summary>
|
||||
Task<Stream> OpenEntryStreamAsync();
|
||||
/// <summary>
|
||||
/// The archive can find all the parts of the archive needed to extract this entry.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
@@ -66,18 +67,21 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
|
||||
}
|
||||
}
|
||||
|
||||
public Stream OpenEntryStream()
|
||||
public Stream OpenEntryStream() => throw new NotSupportedException("Synchronous extraction is not supported. Use OpenEntryStreamAsync instead.");
|
||||
|
||||
|
||||
public async Task<Stream> OpenEntryStreamAsync()
|
||||
{
|
||||
if (IsRarV3)
|
||||
{
|
||||
return new RarStream(
|
||||
return await RarStream.Create(
|
||||
archive.UnpackV1.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
);
|
||||
}
|
||||
|
||||
return new RarStream(
|
||||
return await RarStream.Create(
|
||||
archive.UnpackV2017.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.SevenZip;
|
||||
|
||||
namespace SharpCompress.Archives.SevenZip;
|
||||
@@ -9,6 +10,7 @@ public class SevenZipArchiveEntry : SevenZipEntry, IArchiveEntry
|
||||
: base(part) => Archive = archive;
|
||||
|
||||
public Stream OpenEntryStream() => FilePart.GetCompressedStream();
|
||||
public virtual async Task<Stream> OpenEntryStreamAsync() => await Task.FromResult(OpenEntryStream());
|
||||
|
||||
public IArchive Archive { get; }
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Tar;
|
||||
|
||||
@@ -11,6 +12,7 @@ public class TarArchiveEntry : TarEntry, IArchiveEntry
|
||||
: base(part, compressionType) => Archive = archive;
|
||||
|
||||
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream().NotNull();
|
||||
public virtual async Task<Stream> OpenEntryStreamAsync() => await Task.FromResult(OpenEntryStream());
|
||||
|
||||
#region IArchiveEntry Members
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Zip;
|
||||
|
||||
namespace SharpCompress.Archives.Zip;
|
||||
@@ -10,6 +11,7 @@ public class ZipArchiveEntry : ZipEntry, IArchiveEntry
|
||||
: base(part) => Archive = archive;
|
||||
|
||||
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream().NotNull();
|
||||
public virtual async Task<Stream> OpenEntryStreamAsync() => await Task.FromResult(OpenEntryStream());
|
||||
|
||||
#region IArchiveEntry Members
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
@@ -47,10 +48,18 @@ public class EntryStream : Stream, IStreamStack
|
||||
/// </summary>
|
||||
public void SkipEntry()
|
||||
{
|
||||
this.Skip();
|
||||
this.Skip();
|
||||
_completed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When reading a stream from OpenEntryStream, the stream must be completed so use this to finish reading the entire entry.
|
||||
/// </summary>
|
||||
public async Task SkipEntryAsync()
|
||||
{
|
||||
await this.SkipAsync();
|
||||
_completed = true;
|
||||
}
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!(_completed || _reader.Cancelled))
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
|
||||
namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal interface IRarUnpack
|
||||
{
|
||||
#if NETSTANDARD2_0 || NETFRAMEWORK
|
||||
void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream);
|
||||
void DoUnpack();
|
||||
|
||||
#else
|
||||
ValueTask DoUnpackAsync(FileHeader fileHeader, Stream readStream, Stream writeStream);
|
||||
ValueTask DoUnpackAsync();
|
||||
#endif
|
||||
// eg u/i pause/resume button
|
||||
bool Suspended { get; set; }
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
@@ -9,6 +10,13 @@ namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal class RarBLAKE2spStream : RarStream, IStreamStack
|
||||
{
|
||||
public static async ValueTask<RarBLAKE2spStream> Create(IRarUnpack unpack, FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream)
|
||||
{
|
||||
var rs = new RarBLAKE2spStream(unpack, fileHeader, readStream);
|
||||
await RarStream.Initialize(rs, unpack, fileHeader, readStream);
|
||||
return rs;
|
||||
}
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
@@ -103,7 +111,7 @@ internal class RarBLAKE2spStream : RarStream, IStreamStack
|
||||
|
||||
byte[] _hash = { };
|
||||
|
||||
public RarBLAKE2spStream(
|
||||
protected RarBLAKE2spStream(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#nullable disable
|
||||
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
@@ -36,18 +38,40 @@ internal class RarStream : Stream, IStreamStack
|
||||
|
||||
private bool fetch;
|
||||
|
||||
private byte[] tmpBuffer = ArrayPool<byte>.Shared.Rent(65536);
|
||||
private byte[]? tmpBuffer = ArrayPool<byte>.Shared.Rent(65536);
|
||||
private int tmpOffset;
|
||||
private int tmpCount;
|
||||
|
||||
private byte[] outBuffer;
|
||||
private byte[]? outBuffer;
|
||||
private int outOffset;
|
||||
private int outCount;
|
||||
private int outTotal;
|
||||
private bool isDisposed;
|
||||
private long _position;
|
||||
|
||||
public RarStream(IRarUnpack unpack, FileHeader fileHeader, Stream readStream)
|
||||
public static async ValueTask<RarStream> Create(IRarUnpack unpack, FileHeader fileHeader, Stream readStream)
|
||||
{
|
||||
var rs = new RarStream(unpack, fileHeader, readStream);
|
||||
await Initialize(rs, unpack, fileHeader, readStream);
|
||||
return rs;
|
||||
}
|
||||
|
||||
internal static async ValueTask Initialize(RarStream rs,IRarUnpack unpack, FileHeader fileHeader, Stream readStream)
|
||||
{
|
||||
|
||||
|
||||
rs.fetch = true;
|
||||
#if !NETSTANDARD2_0 && !NETFRAMEWORK
|
||||
await unpack.DoUnpackAsync(fileHeader, readStream, rs);
|
||||
#else
|
||||
unpack.DoUnpack(fileHeader, readStream, rs);
|
||||
await Task.CompletedTask;
|
||||
#endif
|
||||
rs.fetch = false;
|
||||
rs._position = 0;
|
||||
}
|
||||
|
||||
protected RarStream(IRarUnpack unpack, FileHeader fileHeader, Stream readStream)
|
||||
{
|
||||
this.unpack = unpack;
|
||||
this.fileHeader = fileHeader;
|
||||
@@ -56,11 +80,6 @@ internal class RarStream : Stream, IStreamStack
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(RarStream));
|
||||
#endif
|
||||
|
||||
fetch = true;
|
||||
unpack.DoUnpack(fileHeader, readStream, this);
|
||||
fetch = false;
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -72,14 +91,36 @@ internal class RarStream : Stream, IStreamStack
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(RarStream));
|
||||
#endif
|
||||
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
|
||||
this.tmpBuffer = null;
|
||||
if (tmpBuffer != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
|
||||
this.tmpBuffer = null;
|
||||
}
|
||||
}
|
||||
isDisposed = true;
|
||||
base.Dispose(disposing);
|
||||
readStream.Dispose();
|
||||
}
|
||||
}
|
||||
#if !NETSTANDARD2_0 && !NETFRAMEWORK
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (!isDisposed)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(RarStream));
|
||||
#endif
|
||||
if (tmpBuffer != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
|
||||
this.tmpBuffer = null;
|
||||
}
|
||||
isDisposed = true;
|
||||
await readStream.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
@@ -89,6 +130,8 @@ internal class RarStream : Stream, IStreamStack
|
||||
|
||||
public override void Flush() { }
|
||||
|
||||
public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public override long Length => fileHeader.UncompressedSize;
|
||||
|
||||
//commented out code always returned the length of the file
|
||||
@@ -98,8 +141,87 @@ internal class RarStream : Stream, IStreamStack
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#if !NETSTANDARD2_0 && !NETFRAMEWORK
|
||||
|
||||
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var bytesRead = Read(buffer, offset, count);
|
||||
return Task.FromResult(bytesRead);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromException<int>(ex);
|
||||
}
|
||||
}
|
||||
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
outTotal = 0;
|
||||
var count = buffer.Length;
|
||||
var offset = 0;
|
||||
|
||||
if (tmpCount > 0)
|
||||
{
|
||||
var toCopy = tmpCount < count ? tmpCount : count;
|
||||
tmpBuffer.AsSpan(tmpOffset, toCopy).CopyTo(buffer.Span.Slice(offset, toCopy));
|
||||
tmpOffset += toCopy;
|
||||
tmpCount -= toCopy;
|
||||
offset += toCopy;
|
||||
count -= toCopy;
|
||||
outTotal += toCopy;
|
||||
}
|
||||
if (count > 0 && unpack.DestSize > 0)
|
||||
{
|
||||
// Create a temporary array for the unpack operation
|
||||
var tempArray = ArrayPool<byte>.Shared.Rent(count);
|
||||
try
|
||||
{
|
||||
outBuffer = tempArray;
|
||||
outOffset = 0;
|
||||
outCount = count;
|
||||
fetch = true;
|
||||
await unpack.DoUnpackAsync();
|
||||
fetch = false;
|
||||
|
||||
// Copy the unpacked data to the memory buffer
|
||||
var unpacked = outTotal - (tmpCount > 0 ? offset : 0);
|
||||
if (unpacked > 0)
|
||||
{
|
||||
tempArray.AsSpan(0, unpacked).CopyTo(buffer.Span.Slice(offset, unpacked));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(tempArray);
|
||||
outBuffer = null;
|
||||
}
|
||||
}
|
||||
_position += outTotal;
|
||||
if (count > 0 && outTotal == 0 && _position != Length)
|
||||
{
|
||||
// sanity check, eg if we try to decompress a redir entry
|
||||
throw new InvalidOperationException(
|
||||
$"unpacked file size does not match header: expected {Length} found {_position}"
|
||||
);
|
||||
}
|
||||
return outTotal;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException("Use ReadAsync or ReadAsync(Memory<byte>) instead.");
|
||||
#else
|
||||
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (tmpBuffer == null)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(RarStream));
|
||||
}
|
||||
outTotal = 0;
|
||||
if (tmpCount > 0)
|
||||
{
|
||||
@@ -130,6 +252,7 @@ internal class RarStream : Stream, IStreamStack
|
||||
}
|
||||
return outTotal;
|
||||
}
|
||||
#endif
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
|
||||
|
||||
@@ -137,6 +260,14 @@ internal class RarStream : Stream, IStreamStack
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (tmpBuffer == null)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(RarStream));
|
||||
}
|
||||
if (outBuffer == null)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(RarStream));
|
||||
}
|
||||
if (!fetch)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
@@ -165,8 +296,72 @@ internal class RarStream : Stream, IStreamStack
|
||||
}
|
||||
}
|
||||
|
||||
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
Write(buffer, offset, count);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromException(ex);
|
||||
}
|
||||
}
|
||||
#if !NETSTANDARD2_0 && !NETFRAMEWORK
|
||||
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
if (!fetch)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
var count = buffer.Length;
|
||||
var offset = 0;
|
||||
|
||||
if (outCount > 0)
|
||||
{
|
||||
var toCopy = outCount < count ? outCount : count;
|
||||
buffer.Span.Slice(offset, toCopy).CopyTo(outBuffer.AsSpan(outOffset, toCopy));
|
||||
outOffset += toCopy;
|
||||
outCount -= toCopy;
|
||||
offset += toCopy;
|
||||
count -= toCopy;
|
||||
outTotal += toCopy;
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
EnsureBufferCapacity(count);
|
||||
buffer.Span.Slice(offset, count).CopyTo(tmpBuffer.AsSpan(tmpCount, count));
|
||||
tmpCount += count;
|
||||
tmpOffset = 0;
|
||||
unpack.Suspended = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unpack.Suspended = false;
|
||||
}
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ValueTask(Task.FromException(ex));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void EnsureBufferCapacity(int count)
|
||||
{
|
||||
if (tmpBuffer == null)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(RarStream));
|
||||
}
|
||||
if (this.tmpBuffer.Length < this.tmpCount + count)
|
||||
{
|
||||
var newLength =
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.Compressors.PPMd.H;
|
||||
@@ -153,6 +154,20 @@ internal sealed partial class Unpack : BitInput, IRarUnpack, IDisposable
|
||||
DoUnpack();
|
||||
}
|
||||
|
||||
#if !NETSTANDARD2_0 && !NETFRAMEWORK
|
||||
public ValueTask DoUnpackAsync()
|
||||
{
|
||||
DoUnpack();
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
public ValueTask DoUnpackAsync(FileHeader fileHeader, Stream readStream, Stream writeStream)
|
||||
{
|
||||
DoUnpack(fileHeader, readStream, writeStream);
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
#endif
|
||||
|
||||
public void DoUnpack()
|
||||
{
|
||||
if (fileHeader.CompressionMethod == 0)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if NETSTANDARD2_0 || NETFRAMEWORK
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
@@ -64,7 +65,6 @@ internal partial class Unpack : IRarUnpack
|
||||
DoUnpack(fileHeader.CompressionAlgorithm, fileHeader.IsSolid);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnstoreFile()
|
||||
{
|
||||
Span<byte> b = stackalloc byte[(int)Math.Min(0x10000, DestUnpSize)];
|
||||
@@ -106,3 +106,4 @@ internal partial class Unpack : IRarUnpack
|
||||
public static byte[] EnsureCapacity(byte[] array, int length) =>
|
||||
array.Length < length ? new byte[length] : array;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if NETSTANDARD2_0 || NETFRAMEWORK
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
@@ -29,12 +30,12 @@ internal sealed partial class Unpack : BitInput
|
||||
Suspended = false;
|
||||
UnpAllBuf = false;
|
||||
UnpSomeRead = false;
|
||||
/*#if RarV2017_RAR_SMP
|
||||
MaxUserThreads = 1;
|
||||
UnpThreadPool = CreateThreadPool();
|
||||
ReadBufMT = null;
|
||||
UnpThreadData = null;
|
||||
#endif*/
|
||||
// #if RarV2017_RAR_SMP
|
||||
// MaxUserThreads = 1;
|
||||
// UnpThreadPool = CreateThreadPool();
|
||||
// ReadBufMT = null;
|
||||
// UnpThreadData = null;
|
||||
// #endif
|
||||
MaxWinSize = 0;
|
||||
MaxWinMask = 0;
|
||||
|
||||
@@ -197,21 +198,21 @@ internal sealed partial class Unpack : BitInput
|
||||
break;
|
||||
#endif
|
||||
case 50: // RAR 5.0 compression algorithm.
|
||||
/*#if RarV2017_RAR_SMP
|
||||
if (MaxUserThreads > 1)
|
||||
{
|
||||
// We do not use the multithreaded unpack routine to repack RAR archives
|
||||
// in 'suspended' mode, because unlike the single threaded code it can
|
||||
// write more than one dictionary for same loop pass. So we would need
|
||||
// larger buffers of unknown size. Also we do not support multithreading
|
||||
// in fragmented window mode.
|
||||
if (!Fragmented)
|
||||
{
|
||||
Unpack5MT(Solid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif*/
|
||||
// #if RarV2017_RAR_SMP
|
||||
// if (MaxUserThreads > 1)
|
||||
// {
|
||||
// // We do not use the multithreaded unpack routine to repack RAR archives
|
||||
// // in 'suspended' mode, because unlike the single threaded code it can
|
||||
// // write more than one dictionary for same loop pass. So we would need
|
||||
// // larger buffers of unknown size. Also we do not support multithreading
|
||||
// // in fragmented window mode.
|
||||
// if (!Fragmented)
|
||||
// {
|
||||
// Unpack5MT(Solid);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// #endif
|
||||
Unpack5(Solid);
|
||||
break;
|
||||
#if !Rar2017_NOSTRICT
|
||||
@@ -407,3 +408,4 @@ internal sealed partial class Unpack : BitInput
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
#if !NETSTANDARD2_0 && !NETFRAMEWORK
|
||||
|
||||
using System;
|
||||
using SharpCompress.Common;
|
||||
using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef;
|
||||
using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal;
|
||||
#if !Rar2017_64bit
|
||||
using size_t = System.UInt32;
|
||||
#else
|
||||
using nint = System.Int64;
|
||||
using nuint = System.UInt64;
|
||||
using size_t = System.UInt64;
|
||||
#endif
|
||||
|
||||
namespace SharpCompress.Compressors.Rar.UnpackV2017;
|
||||
|
||||
internal sealed partial class Unpack : BitInput
|
||||
{
|
||||
public Unpack( /* ComprDataIO *DataIO */
|
||||
)
|
||||
//:Inp(true),VMCodeInp(true)
|
||||
: base(true)
|
||||
{
|
||||
_UnpackCtor();
|
||||
|
||||
//UnpIO=DataIO;
|
||||
Window = null;
|
||||
Fragmented = false;
|
||||
Suspended = false;
|
||||
UnpAllBuf = false;
|
||||
UnpSomeRead = false;
|
||||
// #if RarV2017_RAR_SMP
|
||||
// MaxUserThreads = 1;
|
||||
// UnpThreadPool = CreateThreadPool();
|
||||
// ReadBufMT = null;
|
||||
// UnpThreadData = null;
|
||||
// #endif
|
||||
MaxWinSize = 0;
|
||||
MaxWinMask = 0;
|
||||
|
||||
// Perform initialization, which should be done only once for all files.
|
||||
// It prevents crash if first DoUnpack call is later made with wrong
|
||||
// (true) 'Solid' value.
|
||||
UnpInitData(false);
|
||||
#if !RarV2017_SFX_MODULE
|
||||
// RAR 1.5 decompression initialization
|
||||
UnpInitData15(false);
|
||||
InitHuff();
|
||||
#endif
|
||||
}
|
||||
|
||||
// later: may need Dispose() if we support thread pool
|
||||
//Unpack::~Unpack()
|
||||
//{
|
||||
// InitFilters30(false);
|
||||
//
|
||||
// if (Window!=null)
|
||||
// free(Window);
|
||||
//#if RarV2017_RAR_SMP
|
||||
// DestroyThreadPool(UnpThreadPool);
|
||||
// delete[] ReadBufMT;
|
||||
// delete[] UnpThreadData;
|
||||
//#endif
|
||||
//}
|
||||
|
||||
private void Init(size_t WinSize, bool Solid)
|
||||
{
|
||||
// If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size
|
||||
// will be 0 because of size_t overflow. Let's issue the memory error.
|
||||
if (WinSize == 0)
|
||||
//ErrHandler.MemoryError();
|
||||
{
|
||||
throw new InvalidFormatException(
|
||||
"invalid window size (possibly due to a rar file with a 4GB being unpacked on a 32-bit platform)"
|
||||
);
|
||||
}
|
||||
|
||||
// Minimum window size must be at least twice more than maximum possible
|
||||
// size of filter block, which is 0x10000 in RAR now. If window size is
|
||||
// smaller, we can have a block with never cleared flt->NextWindow flag
|
||||
// in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's
|
||||
// use 0x40000 for extra safety and possible filter area size expansion.
|
||||
const size_t MinAllocSize = 0x40000;
|
||||
if (WinSize < MinAllocSize)
|
||||
{
|
||||
WinSize = MinAllocSize;
|
||||
}
|
||||
|
||||
if (WinSize <= MaxWinSize) // Use the already allocated window.
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((WinSize >> 16) > 0x10000) // Window size must not exceed 4 GB.
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Archiving code guarantees that window size does not grow in the same
|
||||
// solid stream. So if we are here, we are either creating a new window
|
||||
// or increasing the size of non-solid window. So we could safely reject
|
||||
// current window data without copying them to a new window, though being
|
||||
// extra cautious, we still handle the solid window grow case below.
|
||||
var Grow = Solid && (Window != null || Fragmented);
|
||||
|
||||
// We do not handle growth for existing fragmented window.
|
||||
if (Grow && Fragmented)
|
||||
//throw std::bad_alloc();
|
||||
{
|
||||
throw new InvalidFormatException("Grow && Fragmented");
|
||||
}
|
||||
|
||||
var NewWindow = Fragmented ? null : new byte[WinSize];
|
||||
|
||||
if (NewWindow == null)
|
||||
{
|
||||
if (Grow || WinSize < 0x1000000)
|
||||
{
|
||||
// We do not support growth for new fragmented window.
|
||||
// Also exclude RAR4 and small dictionaries.
|
||||
//throw std::bad_alloc();
|
||||
throw new InvalidFormatException("Grow || WinSize<0x1000000");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Window != null) // If allocated by preceding files.
|
||||
{
|
||||
//free(Window);
|
||||
Window = null;
|
||||
}
|
||||
FragWindow.Init(WinSize);
|
||||
Fragmented = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Fragmented)
|
||||
{
|
||||
// Clean the window to generate the same output when unpacking corrupt
|
||||
// RAR files, which may access unused areas of sliding dictionary.
|
||||
// sharpcompress: don't need this, freshly allocated above
|
||||
//memset(NewWindow,0,WinSize);
|
||||
|
||||
// If Window is not NULL, it means that window size has grown.
|
||||
// In solid streams we need to copy data to a new window in such case.
|
||||
// RAR archiving code does not allow it in solid streams now,
|
||||
// but let's implement it anyway just in case we'll change it sometimes.
|
||||
if (Grow)
|
||||
{
|
||||
for (size_t I = 1; I <= MaxWinSize; I++)
|
||||
{
|
||||
NewWindow[(UnpPtr - I) & (WinSize - 1)] = Window[
|
||||
(UnpPtr - I) & (MaxWinSize - 1)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
//if (Window!=null)
|
||||
// free(Window);
|
||||
Window = NewWindow;
|
||||
}
|
||||
|
||||
MaxWinSize = WinSize;
|
||||
MaxWinMask = MaxWinSize - 1;
|
||||
}
|
||||
|
||||
private void DoUnpack(uint Method, bool Solid)
|
||||
{
|
||||
// Methods <50 will crash in Fragmented mode when accessing NULL Window.
|
||||
// They cannot be called in such mode now, but we check it below anyway
|
||||
// just for extra safety.
|
||||
switch (Method)
|
||||
{
|
||||
#if !RarV2017_SFX_MODULE
|
||||
case 15: // rar 1.5 compression
|
||||
if (!Fragmented)
|
||||
{
|
||||
Unpack15(Solid);
|
||||
}
|
||||
|
||||
break;
|
||||
case 20: // rar 2.x compression
|
||||
case 26: // files larger than 2GB
|
||||
if (!Fragmented)
|
||||
{
|
||||
Unpack20(Solid);
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
#if !RarV2017_RAR5ONLY
|
||||
case 29: // rar 3.x compression
|
||||
if (!Fragmented)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
case 50: // RAR 5.0 compression algorithm.
|
||||
// #if RarV2017_RAR_SMP
|
||||
// if (MaxUserThreads > 1)
|
||||
// {
|
||||
// // We do not use the multithreaded unpack routine to repack RAR archives
|
||||
// // in 'suspended' mode, because unlike the single threaded code it can
|
||||
// // write more than one dictionary for same loop pass. So we would need
|
||||
// // larger buffers of unknown size. Also we do not support multithreading
|
||||
// // in fragmented window mode.
|
||||
// if (!Fragmented)
|
||||
// {
|
||||
// Unpack5MT(Solid);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// #endif
|
||||
Unpack5(Solid);
|
||||
break;
|
||||
#if !Rar2017_NOSTRICT
|
||||
default:
|
||||
throw new InvalidFormatException("unknown compression method " + Method);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void UnpInitData(bool Solid)
|
||||
{
|
||||
if (!Solid)
|
||||
{
|
||||
new Span<uint>(OldDist).Clear();
|
||||
OldDistPtr = 0;
|
||||
LastDist = LastLength = 0;
|
||||
// memset(Window,0,MaxWinSize);
|
||||
//memset(&BlockTables,0,sizeof(BlockTables));
|
||||
BlockTables = new UnpackBlockTables();
|
||||
// sharpcompress: no default ctor for struct
|
||||
BlockTables.Init();
|
||||
UnpPtr = WrPtr = 0;
|
||||
WriteBorder = Math.Min(MaxWinSize, UNPACK_MAX_WRITE) & MaxWinMask;
|
||||
}
|
||||
// Filters never share several solid files, so we can safely reset them
|
||||
// even in solid archive.
|
||||
InitFilters();
|
||||
|
||||
Inp.InitBitInput();
|
||||
WrittenFileSize = 0;
|
||||
ReadTop = 0;
|
||||
ReadBorder = 0;
|
||||
|
||||
//memset(&BlockHeader,0,sizeof(BlockHeader));
|
||||
BlockHeader = new UnpackBlockHeader();
|
||||
BlockHeader.BlockSize = -1; // '-1' means not defined yet.
|
||||
#if !RarV2017_SFX_MODULE
|
||||
UnpInitData20(Solid);
|
||||
#endif
|
||||
//UnpInitData30(Solid);
|
||||
UnpInitData50(Solid);
|
||||
}
|
||||
|
||||
// LengthTable contains the length in bits for every element of alphabet.
|
||||
// Dec is the structure to decode Huffman code/
|
||||
// Size is size of length table and DecodeNum field in Dec structure,
|
||||
private void MakeDecodeTables(Span<byte> LengthTable, int offset, DecodeTable Dec, uint Size)
|
||||
{
|
||||
// Size of alphabet and DecodePos array.
|
||||
Dec.MaxNum = Size;
|
||||
|
||||
// Calculate how many entries for every bit length in LengthTable we have.
|
||||
var LengthCount = new uint[16];
|
||||
//memset(LengthCount,0,sizeof(LengthCount));
|
||||
for (size_t I = 0; I < Size; I++)
|
||||
{
|
||||
LengthCount[LengthTable[checked((int)(offset + I))] & 0xf]++;
|
||||
}
|
||||
|
||||
// We must not calculate the number of zero length codes.
|
||||
LengthCount[0] = 0;
|
||||
|
||||
// Set the entire DecodeNum to zero.
|
||||
//memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum));
|
||||
new Span<ushort>(Dec.DecodeNum).Clear();
|
||||
|
||||
// Initialize not really used entry for zero length code.
|
||||
Dec.DecodePos[0] = 0;
|
||||
|
||||
// Start code for bit length 1 is 0.
|
||||
Dec.DecodeLen[0] = 0;
|
||||
|
||||
// Right aligned upper limit code for current bit length.
|
||||
uint UpperLimit = 0;
|
||||
|
||||
for (var I = 1; I < 16; I++)
|
||||
{
|
||||
// Adjust the upper limit code.
|
||||
UpperLimit += LengthCount[I];
|
||||
|
||||
// Left aligned upper limit code.
|
||||
var LeftAligned = UpperLimit << (16 - I);
|
||||
|
||||
// Prepare the upper limit code for next bit length.
|
||||
UpperLimit *= 2;
|
||||
|
||||
// Store the left aligned upper limit code.
|
||||
Dec.DecodeLen[I] = LeftAligned;
|
||||
|
||||
// Every item of this array contains the sum of all preceding items.
|
||||
// So it contains the start position in code list for every bit length.
|
||||
Dec.DecodePos[I] = Dec.DecodePos[I - 1] + LengthCount[I - 1];
|
||||
}
|
||||
|
||||
// Prepare the copy of DecodePos. We'll modify this copy below,
|
||||
// so we cannot use the original DecodePos.
|
||||
var CopyDecodePos = new uint[Dec.DecodePos.Length];
|
||||
//memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos));
|
||||
Array.Copy(Dec.DecodePos, CopyDecodePos, CopyDecodePos.Length);
|
||||
|
||||
// For every bit length in the bit length table and so for every item
|
||||
// of alphabet.
|
||||
for (uint I = 0; I < Size; I++)
|
||||
{
|
||||
// Get the current bit length.
|
||||
var _CurBitLength = (byte)(LengthTable[checked((int)(offset + I))] & 0xf);
|
||||
|
||||
if (_CurBitLength != 0)
|
||||
{
|
||||
// Last position in code list for current bit length.
|
||||
var LastPos = CopyDecodePos[_CurBitLength];
|
||||
|
||||
// Prepare the decode table, so this position in code list will be
|
||||
// decoded to current alphabet item number.
|
||||
Dec.DecodeNum[LastPos] = (ushort)I;
|
||||
|
||||
// We'll use next position number for this bit length next time.
|
||||
// So we pass through the entire range of positions available
|
||||
// for every bit length.
|
||||
CopyDecodePos[_CurBitLength]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Define the number of bits to process in quick mode. We use more bits
|
||||
// for larger alphabets. More bits means that more codes will be processed
|
||||
// in quick mode, but also that more time will be spent to preparation
|
||||
// of tables for quick decode.
|
||||
switch (Size)
|
||||
{
|
||||
case NC:
|
||||
case NC20:
|
||||
case NC30:
|
||||
Dec.QuickBits = MAX_QUICK_DECODE_BITS;
|
||||
break;
|
||||
default:
|
||||
Dec.QuickBits = MAX_QUICK_DECODE_BITS - 3;
|
||||
break;
|
||||
}
|
||||
|
||||
// Size of tables for quick mode.
|
||||
var QuickDataSize = 1U << (int)Dec.QuickBits;
|
||||
|
||||
// Bit length for current code, start from 1 bit codes. It is important
|
||||
// to use 1 bit instead of 0 for minimum code length, so we are moving
|
||||
// forward even when processing a corrupt archive.
|
||||
//uint CurBitLength=1;
|
||||
byte CurBitLength = 1;
|
||||
|
||||
// For every right aligned bit string which supports the quick decoding.
|
||||
for (uint Code = 0; Code < QuickDataSize; Code++)
|
||||
{
|
||||
// Left align the current code, so it will be in usual bit field format.
|
||||
var BitField = Code << (int)(16 - Dec.QuickBits);
|
||||
|
||||
// Prepare the table for quick decoding of bit lengths.
|
||||
|
||||
// Find the upper limit for current bit field and adjust the bit length
|
||||
// accordingly if necessary.
|
||||
while (CurBitLength < Dec.DecodeLen.Length && BitField >= Dec.DecodeLen[CurBitLength])
|
||||
{
|
||||
CurBitLength++;
|
||||
}
|
||||
|
||||
// Translation of right aligned bit string to bit length.
|
||||
Dec.QuickLen[Code] = CurBitLength;
|
||||
|
||||
// Prepare the table for quick translation of position in code list
|
||||
// to position in alphabet.
|
||||
|
||||
// Calculate the distance from the start code for current bit length.
|
||||
var Dist = BitField - Dec.DecodeLen[CurBitLength - 1];
|
||||
|
||||
// Right align the distance.
|
||||
Dist >>= (16 - CurBitLength);
|
||||
|
||||
// Now we can calculate the position in the code list. It is the sum
|
||||
// of first position for current bit length and right aligned distance
|
||||
// between our bit field and start code for current bit length.
|
||||
uint Pos;
|
||||
if (
|
||||
CurBitLength < Dec.DecodePos.Length
|
||||
&& (Pos = Dec.DecodePos[CurBitLength] + Dist) < Size
|
||||
)
|
||||
{
|
||||
// Define the code to alphabet number translation.
|
||||
Dec.QuickNum[Code] = Dec.DecodeNum[Pos];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Can be here for length table filled with zeroes only (empty).
|
||||
Dec.QuickNum[Code] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
114
src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack_async.cs
Normal file
114
src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack_async.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
#if !NETSTANDARD2_0 && !NETFRAMEWORK
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
#if !Rar2017_64bit
|
||||
using size_t = System.UInt32;
|
||||
#else
|
||||
using nint = System.Int64;
|
||||
using nuint = System.UInt64;
|
||||
using size_t = System.UInt64;
|
||||
#endif
|
||||
|
||||
namespace SharpCompress.Compressors.Rar.UnpackV2017;
|
||||
|
||||
internal partial class Unpack : IRarUnpack
|
||||
{
|
||||
private FileHeader fileHeader;
|
||||
private Stream readStream;
|
||||
private Stream writeStream;
|
||||
|
||||
private void _UnpackCtor()
|
||||
{
|
||||
for (var i = 0; i < AudV.Length; i++)
|
||||
{
|
||||
AudV[i] = new AudioVariables();
|
||||
}
|
||||
}
|
||||
|
||||
private int UnpIO_UnpRead(byte[] buf, int offset, int count) =>
|
||||
// NOTE: caller has logic to check for -1 for error we throw instead.
|
||||
readStream.Read(buf, offset, count);
|
||||
|
||||
private void UnpIO_UnpWrite(byte[] buf, size_t offset, uint count) =>
|
||||
writeStream.Write(buf, checked((int)offset), checked((int)count));
|
||||
|
||||
|
||||
public ValueTask DoUnpackAsync(FileHeader fileHeader, Stream readStream, Stream writeStream)
|
||||
{
|
||||
// as of 12/2017 .NET limits array indexing to using a signed integer
|
||||
// MaxWinSize causes unpack to use a fragmented window when the file
|
||||
// window size exceeds MaxWinSize
|
||||
// uggh, that's not how this variable is used, it's the size of the currently allocated window buffer
|
||||
//x MaxWinSize = ((uint)int.MaxValue) + 1;
|
||||
|
||||
// may be long.MaxValue which could indicate unknown size (not present in header)
|
||||
DestUnpSize = fileHeader.UncompressedSize;
|
||||
this.fileHeader = fileHeader;
|
||||
this.readStream = readStream;
|
||||
this.writeStream = writeStream;
|
||||
if (!fileHeader.IsStored)
|
||||
{
|
||||
Init(fileHeader.WindowSize, fileHeader.IsSolid);
|
||||
}
|
||||
Suspended = false;
|
||||
return DoUnpackAsync();
|
||||
}
|
||||
|
||||
public ValueTask DoUnpackAsync()
|
||||
{
|
||||
if (fileHeader.IsStored)
|
||||
{
|
||||
return UnstoreFileAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
DoUnpack(fileHeader.CompressionAlgorithm, fileHeader.IsSolid);
|
||||
return new ValueTask();
|
||||
}
|
||||
}
|
||||
private async ValueTask UnstoreFileAsync()
|
||||
{
|
||||
var length = (int)Math.Min(0x10000, DestUnpSize);
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(length);
|
||||
do
|
||||
{
|
||||
var memory = new Memory<byte>(buffer, 0, length);
|
||||
var n = await readStream.ReadAsync(memory);
|
||||
if (n == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
await writeStream.WriteAsync(memory.Slice(0, n));
|
||||
DestUnpSize -= n;
|
||||
} while (!Suspended);
|
||||
}
|
||||
public bool Suspended { get; set; }
|
||||
|
||||
public long DestSize => DestUnpSize;
|
||||
|
||||
public int Char
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO: coderb: not sure where the "MAXSIZE-30" comes from, ported from V1 code
|
||||
if (InAddr > MAX_SIZE - 30)
|
||||
{
|
||||
UnpReadBuf();
|
||||
}
|
||||
return InBuf[InAddr++];
|
||||
}
|
||||
}
|
||||
|
||||
public int PpmEscChar
|
||||
{
|
||||
get => PPMEscChar;
|
||||
set => PPMEscChar = value;
|
||||
}
|
||||
|
||||
public static byte[] EnsureCapacity(byte[] array, int length) =>
|
||||
array.Length < length ? new byte[length] : array;
|
||||
}
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Readers;
|
||||
@@ -67,7 +68,7 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveToNextEntry()
|
||||
public async Task<bool> MoveToNextEntryAsync()
|
||||
{
|
||||
if (_completed)
|
||||
{
|
||||
@@ -83,7 +84,7 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
}
|
||||
if (!_wroteCurrentEntry)
|
||||
{
|
||||
SkipEntry();
|
||||
await SkipEntryAsync();
|
||||
}
|
||||
_wroteCurrentEntry = false;
|
||||
if (NextEntryForCurrentStream())
|
||||
@@ -119,15 +120,15 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
|
||||
#region Entry Skip/Write
|
||||
|
||||
private void SkipEntry()
|
||||
private async Task SkipEntryAsync()
|
||||
{
|
||||
if (!Entry.IsDirectory)
|
||||
{
|
||||
Skip();
|
||||
await SkipAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void Skip()
|
||||
private async Task SkipAsync()
|
||||
{
|
||||
var part = Entry.Parts.First();
|
||||
|
||||
@@ -145,11 +146,11 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
}
|
||||
}
|
||||
//don't know the size so we have to try to decompress to skip
|
||||
using var s = OpenEntryStream();
|
||||
s.SkipEntry();
|
||||
using var s = await OpenEntryStreamAsync();
|
||||
await s.SkipEntryAsync();
|
||||
}
|
||||
|
||||
public void WriteEntryTo(Stream writableStream)
|
||||
public async Task WriteEntryToAsync(Stream writableStream)
|
||||
{
|
||||
if (_wroteCurrentEntry)
|
||||
{
|
||||
@@ -167,24 +168,24 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
);
|
||||
}
|
||||
|
||||
Write(writableStream);
|
||||
await WriteAsync(writableStream);
|
||||
_wroteCurrentEntry = true;
|
||||
}
|
||||
|
||||
internal void Write(Stream writeStream)
|
||||
internal async Task WriteAsync(Stream writeStream)
|
||||
{
|
||||
var streamListener = this as IReaderExtractionListener;
|
||||
using Stream s = OpenEntryStream();
|
||||
using Stream s = await OpenEntryStreamAsync();
|
||||
s.TransferTo(writeStream, Entry, streamListener);
|
||||
}
|
||||
|
||||
public EntryStream OpenEntryStream()
|
||||
public async Task<EntryStream> OpenEntryStreamAsync()
|
||||
{
|
||||
if (_wroteCurrentEntry)
|
||||
{
|
||||
throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once.");
|
||||
}
|
||||
var stream = GetEntryStream();
|
||||
var stream = await GetEntryStreamAsync();
|
||||
_wroteCurrentEntry = true;
|
||||
return stream;
|
||||
}
|
||||
@@ -192,11 +193,11 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtracti
|
||||
/// <summary>
|
||||
/// Retains a reference to the entry stream, so we can check whether it completed later.
|
||||
/// </summary>
|
||||
protected EntryStream CreateEntryStream(Stream? decompressed) =>
|
||||
new(this, decompressed.NotNull());
|
||||
protected Task<EntryStream> CreateEntryStreamAsync(Stream? decompressed) =>
|
||||
Task.FromResult(new EntryStream(this, decompressed.NotNull()));
|
||||
|
||||
protected virtual EntryStream GetEntryStream() =>
|
||||
CreateEntryStream(Entry.Parts.First().GetCompressedStream());
|
||||
protected virtual Task<EntryStream> GetEntryStreamAsync() =>
|
||||
CreateEntryStreamAsync(Entry.Parts.First().GetCompressedStream());
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Readers;
|
||||
@@ -19,7 +20,7 @@ public interface IReader : IDisposable
|
||||
/// Decompresses the current entry to the stream. This cannot be called twice for the current entry.
|
||||
/// </summary>
|
||||
/// <param name="writableStream"></param>
|
||||
void WriteEntryTo(Stream writableStream);
|
||||
Task WriteEntryToAsync(Stream writableStream);
|
||||
|
||||
bool Cancelled { get; }
|
||||
void Cancel();
|
||||
@@ -28,11 +29,11 @@ public interface IReader : IDisposable
|
||||
/// Moves to the next entry by reading more data from the underlying stream. This skips if data has not been read.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool MoveToNextEntry();
|
||||
Task<bool> MoveToNextEntryAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Opens the current entry as a stream that will decompress as it is read.
|
||||
/// Read the entire stream or use SkipEntry on EntryStream.
|
||||
/// </summary>
|
||||
EntryStream OpenEntryStream();
|
||||
Task<EntryStream> OpenEntryStreamAsync();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Compressors.Rar;
|
||||
@@ -77,7 +78,7 @@ public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
|
||||
protected virtual IEnumerable<FilePart> CreateFilePartEnumerableForCurrentEntry() =>
|
||||
Entry.Parts;
|
||||
|
||||
protected override EntryStream GetEntryStream()
|
||||
protected override async Task<EntryStream> GetEntryStreamAsync()
|
||||
{
|
||||
if (Entry.IsRedir)
|
||||
{
|
||||
@@ -90,16 +91,17 @@ public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
|
||||
);
|
||||
if (Entry.IsRarV3)
|
||||
{
|
||||
return CreateEntryStream(new RarCrcStream(UnpackV1.Value, Entry.FileHeader, stream));
|
||||
return await CreateEntryStreamAsync(new RarCrcStream(UnpackV1.Value, Entry.FileHeader, stream));
|
||||
}
|
||||
|
||||
if (Entry.FileHeader.FileCrc?.Length > 5)
|
||||
{
|
||||
return CreateEntryStream(
|
||||
new RarBLAKE2spStream(UnpackV2017.Value, Entry.FileHeader, stream)
|
||||
var s = await RarBLAKE2spStream.Create(UnpackV2017.Value, Entry.FileHeader, stream);
|
||||
return await CreateEntryStreamAsync(
|
||||
s
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, Entry.FileHeader, stream));
|
||||
return await CreateEntryStreamAsync(new RarCrcStream(UnpackV2017.Value, Entry.FileHeader, stream));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Helpers;
|
||||
@@ -173,6 +174,19 @@ internal static class Utility
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task SkipAsync(this Stream source)
|
||||
{
|
||||
var buffer = GetTransferByteArray();
|
||||
try
|
||||
{
|
||||
do { } while (await source.ReadAsync(buffer, 0, buffer.Length) == buffer.Length);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Find(this Stream source, byte[] array)
|
||||
{
|
||||
var buffer = GetTransferByteArray();
|
||||
|
||||
Reference in New Issue
Block a user