mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-25 08:24:58 +00:00
Refactor RAR to create new Unpack instances per stream for multi-threading support
- For non-solid archives: create new IRarUnpack instance per RarStream to enable multi-threading - For solid archives: use shared Unpack instance (solid archives require sequential processing) - Add ownsUnpack parameter to RarStream to properly manage disposal of Unpack instances - RarStream now disposes Unpack only if it owns the instance Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
@@ -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());
|
||||
@@ -89,6 +91,7 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
}
|
||||
|
||||
public override bool IsSolid => Volumes.First().IsSolidArchive;
|
||||
public override bool SupportsMultiThreading => !IsMultiVolume && !IsSolid;
|
||||
|
||||
public virtual int MinVersion => Volumes.First().MinVersion;
|
||||
public virtual int MaxVersion => Volumes.First().MaxVersion;
|
||||
|
||||
@@ -70,48 +70,62 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
|
||||
|
||||
public Stream OpenEntryStream()
|
||||
{
|
||||
RarStream stream;
|
||||
if (IsRarV3)
|
||||
IRarUnpack unpack;
|
||||
bool ownsUnpack;
|
||||
|
||||
// For solid archives, use shared Unpack instance (must be processed sequentially)
|
||||
// For non-solid archives, create new instance per stream to support multi-threading
|
||||
if (archive.IsSolid)
|
||||
{
|
||||
stream = new RarStream(
|
||||
archive.UnpackV1.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
);
|
||||
unpack = IsRarV3 ? archive.UnpackV1.Value : archive.UnpackV2017.Value;
|
||||
ownsUnpack = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = new RarStream(
|
||||
archive.UnpackV2017.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
);
|
||||
unpack = IsRarV3
|
||||
? new Compressors.Rar.UnpackV1.Unpack()
|
||||
: new Compressors.Rar.UnpackV2017.Unpack();
|
||||
ownsUnpack = true;
|
||||
}
|
||||
|
||||
var stream = new RarStream(
|
||||
unpack,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive),
|
||||
ownsUnpack
|
||||
);
|
||||
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
|
||||
public async Task<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
RarStream stream;
|
||||
if (IsRarV3)
|
||||
IRarUnpack unpack;
|
||||
bool ownsUnpack;
|
||||
|
||||
// For solid archives, use shared Unpack instance (must be processed sequentially)
|
||||
// For non-solid archives, create new instance per stream to support multi-threading
|
||||
if (archive.IsSolid)
|
||||
{
|
||||
stream = new RarStream(
|
||||
archive.UnpackV1.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
);
|
||||
unpack = IsRarV3 ? archive.UnpackV1.Value : archive.UnpackV2017.Value;
|
||||
ownsUnpack = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = new RarStream(
|
||||
archive.UnpackV2017.Value,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive)
|
||||
);
|
||||
unpack = IsRarV3
|
||||
? new Compressors.Rar.UnpackV1.Unpack()
|
||||
: new Compressors.Rar.UnpackV2017.Unpack();
|
||||
ownsUnpack = true;
|
||||
}
|
||||
|
||||
var stream = new RarStream(
|
||||
unpack,
|
||||
FileHeader,
|
||||
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>(), archive),
|
||||
ownsUnpack
|
||||
);
|
||||
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
return stream;
|
||||
}
|
||||
@@ -134,4 +148,6 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsMultiThreading => Parts.Single().SupportsMultiThreading;
|
||||
}
|
||||
|
||||
@@ -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,17 @@ internal class RarStream : Stream, IStreamStack
|
||||
private bool isDisposed;
|
||||
private long _position;
|
||||
|
||||
public RarStream(IRarUnpack unpack, FileHeader fileHeader, Stream readStream)
|
||||
public RarStream(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
Stream readStream,
|
||||
bool ownsUnpack = false
|
||||
)
|
||||
{
|
||||
this.unpack = unpack;
|
||||
this.fileHeader = fileHeader;
|
||||
this.readStream = readStream;
|
||||
this.ownsUnpack = ownsUnpack;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(RarStream));
|
||||
@@ -84,6 +91,12 @@ internal class RarStream : Stream, IStreamStack
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
|
||||
this.tmpBuffer = null;
|
||||
|
||||
// Only dispose the unpack instance if we own it
|
||||
if (ownsUnpack && unpack is IDisposable disposableUnpack)
|
||||
{
|
||||
disposableUnpack.Dispose();
|
||||
}
|
||||
}
|
||||
isDisposed = true;
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -293,9 +293,15 @@ public class RarArchiveTests : ArchiveTests
|
||||
[Fact]
|
||||
public void Rar_ArchiveFileRead() => ArchiveFileRead("Rar.rar");
|
||||
|
||||
[Fact]
|
||||
public Task Rar_ArchiveFileRead_Multithreaded() => ArchiveFileRead_Multithreaded("Rar.rar");
|
||||
|
||||
[Fact]
|
||||
public void Rar5_ArchiveFileRead() => ArchiveFileRead("Rar5.rar");
|
||||
|
||||
[Fact]
|
||||
public Task Rar5_ArchiveFileRead_Multithreaded() => ArchiveFileRead_Multithreaded("Rar5.rar");
|
||||
|
||||
[Fact]
|
||||
public void Rar_ArchiveFileRead_HasDirectories() =>
|
||||
DoRar_ArchiveFileRead_HasDirectories("Rar.rar");
|
||||
@@ -360,6 +366,9 @@ public class RarArchiveTests : ArchiveTests
|
||||
[Fact]
|
||||
public void Rar2_ArchiveFileRead() => ArchiveFileRead("Rar2.rar");
|
||||
|
||||
[Fact]
|
||||
public Task Rar2_ArchiveFileRead_Multithreaded() => ArchiveFileRead_Multithreaded("Rar2.rar");
|
||||
|
||||
[Fact]
|
||||
public void Rar15_ArchiveFileRead()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user