mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-10 21:22:11 +00:00
Compare commits
5 Commits
adam/multi
...
copilot/su
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a7472146c | ||
|
|
3ec4035d55 | ||
|
|
acffccd889 | ||
|
|
31fd8f46d8 | ||
|
|
ac32f2eb1f |
@@ -15,6 +15,8 @@ namespace SharpCompress.Archives.Rar;
|
||||
public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
// Shared Unpack instances for solid archives (must be used sequentially)
|
||||
internal Lazy<IRarUnpack> UnpackV2017 { get; } =
|
||||
new(() => new Compressors.Rar.UnpackV2017.Unpack());
|
||||
internal Lazy<IRarUnpack> UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack());
|
||||
|
||||
@@ -70,50 +70,50 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
|
||||
|
||||
public Stream OpenEntryStream()
|
||||
{
|
||||
RarStream stream;
|
||||
if (IsRarV3)
|
||||
var readStream = new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive);
|
||||
|
||||
// For solid archives, use shared Unpack instance (must be processed sequentially)
|
||||
// For non-solid archives, use factory to create owned instance (supports multi-threading)
|
||||
if (archive.IsSolid)
|
||||
{
|
||||
stream = new RarStream(
|
||||
archive.UnpackV1.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
);
|
||||
var unpack = IsRarV3 ? archive.UnpackV1.Value : archive.UnpackV2017.Value;
|
||||
var stream = new RarStream(unpack, FileHeader, readStream, ownsUnpack: false);
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = new RarStream(
|
||||
archive.UnpackV2017.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
);
|
||||
var factory = IsRarV3
|
||||
? (IRarUnpackFactory)UnpackV1Factory.Instance
|
||||
: UnpackV2017Factory.Instance;
|
||||
var stream = new RarStream(factory, FileHeader, readStream);
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
|
||||
public async Task<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
RarStream stream;
|
||||
if (IsRarV3)
|
||||
var readStream = new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive);
|
||||
|
||||
// For solid archives, use shared Unpack instance (must be processed sequentially)
|
||||
// For non-solid archives, use factory to create owned instance (supports multi-threading)
|
||||
if (archive.IsSolid)
|
||||
{
|
||||
stream = new RarStream(
|
||||
archive.UnpackV1.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
);
|
||||
var unpack = IsRarV3 ? archive.UnpackV1.Value : archive.UnpackV2017.Value;
|
||||
var stream = new RarStream(unpack, FileHeader, readStream, ownsUnpack: false);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = new RarStream(
|
||||
archive.UnpackV2017.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
);
|
||||
var factory = IsRarV3
|
||||
? (IRarUnpackFactory)UnpackV1Factory.Instance
|
||||
: UnpackV2017Factory.Instance;
|
||||
var stream = new RarStream(factory, FileHeader, readStream);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public bool IsComplete
|
||||
@@ -135,5 +135,6 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsMultiThreading => Parts.Single().SupportsMultiThreading;
|
||||
public override bool SupportsMultiThreading =>
|
||||
!archive.IsSolid && Parts.Single().SupportsMultiThreading;
|
||||
}
|
||||
|
||||
34
src/SharpCompress/Compressors/Rar/IRarUnpackFactory.cs
Normal file
34
src/SharpCompress/Compressors/Rar/IRarUnpackFactory.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
/// <summary>
|
||||
/// Factory interface for creating IRarUnpack instances.
|
||||
/// Each created instance is owned by the caller and should be disposed when done.
|
||||
/// </summary>
|
||||
internal interface IRarUnpackFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new IRarUnpack instance.
|
||||
/// The caller is responsible for disposing the returned instance.
|
||||
/// </summary>
|
||||
IRarUnpack Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory for creating UnpackV1 instances (RAR v3 and earlier).
|
||||
/// </summary>
|
||||
internal sealed class UnpackV1Factory : IRarUnpackFactory
|
||||
{
|
||||
public static readonly UnpackV1Factory Instance = new();
|
||||
|
||||
public IRarUnpack Create() => new UnpackV1.Unpack();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory for creating UnpackV2017 instances (RAR v5+).
|
||||
/// </summary>
|
||||
internal sealed class UnpackV2017Factory : IRarUnpackFactory
|
||||
{
|
||||
public static readonly UnpackV2017Factory Instance = new();
|
||||
|
||||
public IRarUnpack Create() => new UnpackV2017.Unpack();
|
||||
}
|
||||
@@ -106,11 +106,30 @@ internal class RarBLAKE2spStream : RarStream, IStreamStack
|
||||
byte[] _hash = { };
|
||||
|
||||
private RarBLAKE2spStream(
|
||||
IRarUnpack unpack,
|
||||
IRarUnpackFactory unpackFactory,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream
|
||||
)
|
||||
: base(unpack, fileHeader, readStream)
|
||||
: base(unpackFactory, fileHeader, readStream)
|
||||
{
|
||||
this.readStream = readStream;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(RarBLAKE2spStream));
|
||||
#endif
|
||||
disableCRCCheck = fileHeader.IsEncrypted;
|
||||
_hash = fileHeader.FileCrc.NotNull();
|
||||
_blake2sp = new BLAKE2SP();
|
||||
ResetCrc();
|
||||
}
|
||||
|
||||
private RarBLAKE2spStream(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream,
|
||||
bool ownsUnpack
|
||||
)
|
||||
: base(unpack, fileHeader, readStream, ownsUnpack)
|
||||
{
|
||||
this.readStream = readStream;
|
||||
|
||||
@@ -124,24 +143,49 @@ internal class RarBLAKE2spStream : RarStream, IStreamStack
|
||||
}
|
||||
|
||||
public static RarBLAKE2spStream Create(
|
||||
IRarUnpack unpack,
|
||||
IRarUnpackFactory unpackFactory,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream
|
||||
)
|
||||
{
|
||||
var stream = new RarBLAKE2spStream(unpack, fileHeader, readStream);
|
||||
var stream = new RarBLAKE2spStream(unpackFactory, fileHeader, readStream);
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static RarBLAKE2spStream Create(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream,
|
||||
bool ownsUnpack
|
||||
)
|
||||
{
|
||||
var stream = new RarBLAKE2spStream(unpack, fileHeader, readStream, ownsUnpack);
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static async Task<RarBLAKE2spStream> CreateAsync(
|
||||
IRarUnpackFactory unpackFactory,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var stream = new RarBLAKE2spStream(unpackFactory, fileHeader, readStream);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static async Task<RarBLAKE2spStream> CreateAsync(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream,
|
||||
bool ownsUnpack,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var stream = new RarBLAKE2spStream(unpack, fileHeader, readStream);
|
||||
var stream = new RarBLAKE2spStream(unpack, fileHeader, readStream, ownsUnpack);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
|
||||
@@ -34,11 +34,27 @@ internal class RarCrcStream : RarStream, IStreamStack
|
||||
private readonly bool disableCRC;
|
||||
|
||||
private RarCrcStream(
|
||||
IRarUnpack unpack,
|
||||
IRarUnpackFactory unpackFactory,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream
|
||||
)
|
||||
: base(unpack, fileHeader, readStream)
|
||||
: base(unpackFactory, fileHeader, readStream)
|
||||
{
|
||||
this.readStream = readStream;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(RarCrcStream));
|
||||
#endif
|
||||
disableCRC = fileHeader.IsEncrypted;
|
||||
ResetCrc();
|
||||
}
|
||||
|
||||
private RarCrcStream(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream,
|
||||
bool ownsUnpack
|
||||
)
|
||||
: base(unpack, fileHeader, readStream, ownsUnpack)
|
||||
{
|
||||
this.readStream = readStream;
|
||||
#if DEBUG_STREAMS
|
||||
@@ -49,24 +65,49 @@ internal class RarCrcStream : RarStream, IStreamStack
|
||||
}
|
||||
|
||||
public static RarCrcStream Create(
|
||||
IRarUnpack unpack,
|
||||
IRarUnpackFactory unpackFactory,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream
|
||||
)
|
||||
{
|
||||
var stream = new RarCrcStream(unpack, fileHeader, readStream);
|
||||
var stream = new RarCrcStream(unpackFactory, fileHeader, readStream);
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static RarCrcStream Create(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream,
|
||||
bool ownsUnpack
|
||||
)
|
||||
{
|
||||
var stream = new RarCrcStream(unpack, fileHeader, readStream, ownsUnpack);
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static async Task<RarCrcStream> CreateAsync(
|
||||
IRarUnpackFactory unpackFactory,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var stream = new RarCrcStream(unpackFactory, fileHeader, readStream);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static async Task<RarCrcStream> CreateAsync(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStream readStream,
|
||||
bool ownsUnpack,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var stream = new RarCrcStream(unpack, fileHeader, readStream);
|
||||
var stream = new RarCrcStream(unpack, fileHeader, readStream, ownsUnpack);
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ internal class RarStream : Stream, IStreamStack
|
||||
private readonly IRarUnpack unpack;
|
||||
private readonly FileHeader fileHeader;
|
||||
private readonly Stream readStream;
|
||||
private readonly bool ownsUnpack;
|
||||
|
||||
private bool fetch;
|
||||
|
||||
@@ -49,11 +50,28 @@ internal class RarStream : Stream, IStreamStack
|
||||
private bool isDisposed;
|
||||
private long _position;
|
||||
|
||||
public RarStream(IRarUnpack unpack, FileHeader fileHeader, Stream readStream)
|
||||
/// <summary>
|
||||
/// Creates a new RarStream that owns and will dispose its IRarUnpack instance.
|
||||
/// </summary>
|
||||
/// <param name="unpackFactory">Factory to create the IRarUnpack instance</param>
|
||||
/// <param name="fileHeader">File header for the entry</param>
|
||||
/// <param name="readStream">Stream to read compressed data from</param>
|
||||
public RarStream(IRarUnpackFactory unpackFactory, FileHeader fileHeader, Stream readStream)
|
||||
: this(unpackFactory.Create(), fileHeader, readStream, ownsUnpack: true) { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new RarStream with the specified unpack instance.
|
||||
/// </summary>
|
||||
/// <param name="unpack">The IRarUnpack instance to use</param>
|
||||
/// <param name="fileHeader">File header for the entry</param>
|
||||
/// <param name="readStream">Stream to read compressed data from</param>
|
||||
/// <param name="ownsUnpack">Whether this stream should dispose the unpack instance</param>
|
||||
internal RarStream(IRarUnpack unpack, FileHeader fileHeader, Stream readStream, bool ownsUnpack)
|
||||
{
|
||||
this.unpack = unpack;
|
||||
this.fileHeader = fileHeader;
|
||||
this.readStream = readStream;
|
||||
this.ownsUnpack = ownsUnpack;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(RarStream));
|
||||
@@ -84,6 +102,12 @@ internal class RarStream : Stream, IStreamStack
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
|
||||
this.tmpBuffer = null;
|
||||
|
||||
// Dispose the unpack instance if we own it
|
||||
if (ownsUnpack && unpack is IDisposable disposableUnpack)
|
||||
{
|
||||
disposableUnpack.Dispose();
|
||||
}
|
||||
}
|
||||
isDisposed = true;
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -15,6 +15,8 @@ public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
|
||||
{
|
||||
private bool _disposed;
|
||||
private RarVolume? volume;
|
||||
|
||||
// Shared Unpack instances for solid archives (must be used sequentially)
|
||||
private Lazy<IRarUnpack> UnpackV2017 { get; } =
|
||||
new(() => new Compressors.Rar.UnpackV2017.Unpack());
|
||||
private Lazy<IRarUnpack> UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack());
|
||||
@@ -111,19 +113,50 @@ public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
|
||||
CreateFilePartEnumerableForCurrentEntry().Cast<RarFilePart>(),
|
||||
this
|
||||
);
|
||||
if (Entry.IsRarV3)
|
||||
{
|
||||
return CreateEntryStream(RarCrcStream.Create(UnpackV1.Value, Entry.FileHeader, stream));
|
||||
}
|
||||
|
||||
if (Entry.FileHeader.FileCrc?.Length > 5)
|
||||
// For solid archives, use shared Unpack instance (must be processed sequentially)
|
||||
// For non-solid archives, use factory to create owned instance
|
||||
if (Entry.IsSolid || Entry.FileHeader.IsSolid)
|
||||
{
|
||||
var unpack = Entry.IsRarV3 ? UnpackV1.Value : UnpackV2017.Value;
|
||||
if (Entry.IsRarV3)
|
||||
{
|
||||
return CreateEntryStream(
|
||||
RarCrcStream.Create(unpack, Entry.FileHeader, stream, ownsUnpack: false)
|
||||
);
|
||||
}
|
||||
|
||||
if (Entry.FileHeader.FileCrc?.Length > 5)
|
||||
{
|
||||
return CreateEntryStream(
|
||||
RarBLAKE2spStream.Create(unpack, Entry.FileHeader, stream, ownsUnpack: false)
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(
|
||||
RarBLAKE2spStream.Create(UnpackV2017.Value, Entry.FileHeader, stream)
|
||||
RarCrcStream.Create(unpack, Entry.FileHeader, stream, ownsUnpack: false)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
var factory = Entry.IsRarV3
|
||||
? (IRarUnpackFactory)UnpackV1Factory.Instance
|
||||
: UnpackV2017Factory.Instance;
|
||||
|
||||
return CreateEntryStream(RarCrcStream.Create(UnpackV2017.Value, Entry.FileHeader, stream));
|
||||
if (Entry.IsRarV3)
|
||||
{
|
||||
return CreateEntryStream(RarCrcStream.Create(factory, Entry.FileHeader, stream));
|
||||
}
|
||||
|
||||
if (Entry.FileHeader.FileCrc?.Length > 5)
|
||||
{
|
||||
return CreateEntryStream(
|
||||
RarBLAKE2spStream.Create(factory, Entry.FileHeader, stream)
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(RarCrcStream.Create(factory, Entry.FileHeader, stream));
|
||||
}
|
||||
}
|
||||
|
||||
protected override async System.Threading.Tasks.Task<EntryStream> GetEntryStreamAsync(
|
||||
@@ -139,28 +172,83 @@ public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
|
||||
CreateFilePartEnumerableForCurrentEntry().Cast<RarFilePart>(),
|
||||
this
|
||||
);
|
||||
if (Entry.IsRarV3)
|
||||
|
||||
// For solid archives, use shared Unpack instance (must be processed sequentially)
|
||||
// For non-solid archives, use factory to create owned instance
|
||||
if (Entry.IsSolid || Entry.FileHeader.IsSolid)
|
||||
{
|
||||
var unpack = Entry.IsRarV3 ? UnpackV1.Value : UnpackV2017.Value;
|
||||
if (Entry.IsRarV3)
|
||||
{
|
||||
return CreateEntryStream(
|
||||
await RarCrcStream
|
||||
.CreateAsync(
|
||||
unpack,
|
||||
Entry.FileHeader,
|
||||
stream,
|
||||
ownsUnpack: false,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
|
||||
if (Entry.FileHeader.FileCrc?.Length > 5)
|
||||
{
|
||||
return CreateEntryStream(
|
||||
await RarBLAKE2spStream
|
||||
.CreateAsync(
|
||||
unpack,
|
||||
Entry.FileHeader,
|
||||
stream,
|
||||
ownsUnpack: false,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(
|
||||
await RarCrcStream
|
||||
.CreateAsync(UnpackV1.Value, Entry.FileHeader, stream, cancellationToken)
|
||||
.CreateAsync(
|
||||
unpack,
|
||||
Entry.FileHeader,
|
||||
stream,
|
||||
ownsUnpack: false,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
|
||||
if (Entry.FileHeader.FileCrc?.Length > 5)
|
||||
else
|
||||
{
|
||||
var factory = Entry.IsRarV3
|
||||
? (IRarUnpackFactory)UnpackV1Factory.Instance
|
||||
: UnpackV2017Factory.Instance;
|
||||
|
||||
if (Entry.IsRarV3)
|
||||
{
|
||||
return CreateEntryStream(
|
||||
await RarCrcStream
|
||||
.CreateAsync(factory, Entry.FileHeader, stream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
|
||||
if (Entry.FileHeader.FileCrc?.Length > 5)
|
||||
{
|
||||
return CreateEntryStream(
|
||||
await RarBLAKE2spStream
|
||||
.CreateAsync(factory, Entry.FileHeader, stream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(
|
||||
await RarBLAKE2spStream
|
||||
.CreateAsync(UnpackV2017.Value, Entry.FileHeader, stream, cancellationToken)
|
||||
await RarCrcStream
|
||||
.CreateAsync(factory, Entry.FileHeader, stream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(
|
||||
await RarCrcStream
|
||||
.CreateAsync(UnpackV2017.Value, Entry.FileHeader, stream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user