mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-08 18:16:30 +00:00
reduce Rar allocations by stackalloc and pooling
This commit is contained in:
@@ -45,6 +45,10 @@ public partial class RarArchive
|
||||
{
|
||||
unpackV1.Dispose();
|
||||
}
|
||||
if (UnpackV2017.IsValueCreated && UnpackV2017.Value is IDisposable unpackV2017)
|
||||
{
|
||||
unpackV2017.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
base.Dispose();
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal partial class RarBLAKE2spStream : RarStream
|
||||
{
|
||||
public static async ValueTask<RarBLAKE2spStream> CreateAsync(
|
||||
public static ValueTask<RarBLAKE2spStream> CreateAsync(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyAsyncStream readStream,
|
||||
@@ -16,8 +16,7 @@ internal partial class RarBLAKE2spStream : RarStream
|
||||
)
|
||||
{
|
||||
var stream = new RarBLAKE2spStream(unpack, fileHeader, readStream);
|
||||
await stream.InitializeAsync(cancellationToken).ConfigureAwait(false);
|
||||
return stream;
|
||||
return new ValueTask<RarBLAKE2spStream>(stream);
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(
|
||||
|
||||
@@ -100,7 +100,6 @@ internal partial class RarBLAKE2spStream : RarStream
|
||||
)
|
||||
{
|
||||
var stream = new RarBLAKE2spStream(unpack, fileHeader, readStream);
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal partial class RarCrcStream : RarStream
|
||||
{
|
||||
public static async ValueTask<RarCrcStream> CreateAsync(
|
||||
public static ValueTask<RarCrcStream> CreateAsync(
|
||||
IRarUnpack unpack,
|
||||
FileHeader fileHeader,
|
||||
MultiVolumeReadOnlyStreamBase readStream,
|
||||
@@ -17,8 +17,7 @@ internal partial class RarCrcStream : RarStream
|
||||
)
|
||||
{
|
||||
var stream = new RarCrcStream(unpack, fileHeader, readStream);
|
||||
await stream.InitializeAsync(cancellationToken).ConfigureAwait(false);
|
||||
return stream;
|
||||
return new ValueTask<RarCrcStream>(stream);
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(
|
||||
|
||||
@@ -32,7 +32,6 @@ internal partial class RarCrcStream : RarStream
|
||||
)
|
||||
{
|
||||
var stream = new RarCrcStream(unpack, fileHeader, readStream);
|
||||
stream.Initialize();
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
@@ -18,11 +19,17 @@ internal partial class RarStream
|
||||
/// </summary>
|
||||
public async ValueTask InitializeAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fetch = true;
|
||||
await unpack
|
||||
.DoUnpackAsync(fileHeader, readStream, this, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
fetch = false;
|
||||
initialized = true;
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
@@ -46,6 +53,12 @@ internal partial class RarStream
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
await InitializeAsync(cancellationToken).ConfigureAwait(false);
|
||||
outTotal = 0;
|
||||
if (tmpCount > 0)
|
||||
{
|
||||
@@ -87,6 +100,17 @@ internal partial class RarStream
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (MemoryMarshal.TryGetArray<byte>(buffer, out var segment))
|
||||
{
|
||||
return await ReadImplAsync(
|
||||
segment.Array!,
|
||||
segment.Offset,
|
||||
buffer.Length,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var array = ArrayPool<byte>.Shared.Rent(buffer.Length);
|
||||
try
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@ internal partial class RarStream : Stream
|
||||
private int outOffset;
|
||||
private int outCount;
|
||||
private int outTotal;
|
||||
private bool initialized;
|
||||
private bool isDisposed;
|
||||
private long _position;
|
||||
|
||||
@@ -36,9 +37,15 @@ internal partial class RarStream : Stream
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fetch = true;
|
||||
unpack.DoUnpack(fileHeader, readStream, this);
|
||||
fetch = false;
|
||||
initialized = true;
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
@@ -76,6 +83,12 @@ internal partial class RarStream : Stream
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Initialize();
|
||||
outTotal = 0;
|
||||
if (tmpCount > 0)
|
||||
{
|
||||
|
||||
@@ -65,20 +65,29 @@ internal sealed partial class Unpack
|
||||
|
||||
private async ValueTask UnstoreFileAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var buffer = new byte[(int)Math.Min(0x10000, destUnpSize)];
|
||||
do
|
||||
var buffer = ArrayPool<byte>.Shared.Rent((int)Math.Min(0x10000, destUnpSize));
|
||||
try
|
||||
{
|
||||
var code = await readStream
|
||||
.ReadAsync(buffer, 0, buffer.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (code == 0 || code == -1)
|
||||
do
|
||||
{
|
||||
break;
|
||||
}
|
||||
code = code < destUnpSize ? code : (int)destUnpSize;
|
||||
await writeStream.WriteAsync(buffer, 0, code, cancellationToken).ConfigureAwait(false);
|
||||
destUnpSize -= code;
|
||||
} while (!suspended && destUnpSize > 0);
|
||||
var code = await readStream
|
||||
.ReadAsync(buffer, 0, buffer.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (code == 0 || code == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
code = code < destUnpSize ? code : (int)destUnpSize;
|
||||
await writeStream
|
||||
.WriteAsync(buffer, 0, code, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
destUnpSize -= code;
|
||||
} while (!suspended && destUnpSize > 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask Unpack29Async(bool solid, CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using size_t = System.UInt32;
|
||||
using System.Buffers;
|
||||
using size_t = System.UInt32;
|
||||
|
||||
#nullable disable
|
||||
|
||||
@@ -16,7 +17,7 @@ internal partial class BitInput
|
||||
// read only 1 byte from the last position of buffer and avoid a crash
|
||||
// from access to next 3 bytes, which contents we do not need.
|
||||
size_t BufSize = MAX_SIZE + 3;
|
||||
InBuf = new byte[BufSize];
|
||||
InBuf = ArrayPool<byte>.Shared.Rent(checked((int)BufSize));
|
||||
|
||||
// Ensure that we get predictable results when accessing bytes in area
|
||||
// not filled with read data.
|
||||
@@ -42,4 +43,13 @@ internal partial class BitInput
|
||||
public uint fgetbits() =>
|
||||
// Function wrapped version of inline getbits to save code size.
|
||||
getbits();
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (!ExternalBuffer && InBuf != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(InBuf);
|
||||
InBuf = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
namespace SharpCompress.Compressors.Rar.UnpackV2017;
|
||||
using System;
|
||||
|
||||
internal partial class BitInput
|
||||
namespace SharpCompress.Compressors.Rar.UnpackV2017;
|
||||
|
||||
internal partial class BitInput : IDisposable
|
||||
{
|
||||
public const int MAX_SIZE = 0x8000; // Size of input buffer.
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using SharpCompress.Common;
|
||||
using size_t = System.UInt32;
|
||||
|
||||
@@ -19,15 +20,16 @@ internal partial class FragmentedWindow
|
||||
// Reset();
|
||||
//}
|
||||
|
||||
private void Reset()
|
||||
public void Reset()
|
||||
{
|
||||
for (uint I = 0; I < Mem.Length; I++)
|
||||
{
|
||||
if (Mem[I] != null)
|
||||
{
|
||||
//free(Mem[I]);
|
||||
ArrayPool<byte>.Shared.Return(Mem[I]);
|
||||
Mem[I] = null;
|
||||
}
|
||||
MemSize[I] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +52,7 @@ internal partial class FragmentedWindow
|
||||
byte[] NewMem = null;
|
||||
while (Size >= MinSize)
|
||||
{
|
||||
NewMem = new byte[Size];
|
||||
NewMem = ArrayPool<byte>.Shared.Rent(checked((int)Size));
|
||||
if (NewMem != null)
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -135,19 +136,26 @@ internal partial class Unpack : IRarUnpack
|
||||
|
||||
private async ValueTask UnstoreFileAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var buffer = new byte[(int)Math.Min(0x10000, DestUnpSize)];
|
||||
do
|
||||
var buffer = ArrayPool<byte>.Shared.Rent((int)Math.Min(0x10000, DestUnpSize));
|
||||
try
|
||||
{
|
||||
var n = await readStream
|
||||
.ReadAsync(buffer, 0, buffer.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (n == 0)
|
||||
do
|
||||
{
|
||||
break;
|
||||
}
|
||||
await writeStream.WriteAsync(buffer, 0, n, cancellationToken).ConfigureAwait(false);
|
||||
DestUnpSize -= n;
|
||||
} while (!Suspended);
|
||||
var n = await readStream
|
||||
.ReadAsync(buffer, 0, buffer.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (n == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
await writeStream.WriteAsync(buffer, 0, n, cancellationToken).ConfigureAwait(false);
|
||||
DestUnpSize -= n;
|
||||
} while (!Suspended);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Suspended { get; set; }
|
||||
@@ -181,6 +189,18 @@ internal partial class Unpack : IRarUnpack
|
||||
set => PPMEscChar = value;
|
||||
}
|
||||
|
||||
public static byte[] EnsureCapacity(byte[] array, int length) =>
|
||||
array.Length < length ? new byte[length] : array;
|
||||
private byte[] EnsureCapacity(byte[] array, int length)
|
||||
{
|
||||
if (array.Length >= length)
|
||||
{
|
||||
return array;
|
||||
}
|
||||
|
||||
var newArray = ArrayPool<byte>.Shared.Rent(length);
|
||||
if (array.Length != 0)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017;
|
||||
|
||||
internal sealed partial class Unpack : BitInput
|
||||
{
|
||||
private bool disposed;
|
||||
|
||||
public Unpack( /* ComprDataIO *DataIO */
|
||||
)
|
||||
//:Inp(true),VMCodeInp(true)
|
||||
@@ -44,19 +46,32 @@ internal sealed partial class Unpack : BitInput
|
||||
InitHuff();
|
||||
}
|
||||
|
||||
// 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
|
||||
//}
|
||||
public override void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
if (Window != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(Window);
|
||||
Window = null;
|
||||
}
|
||||
FragWindow.Reset();
|
||||
if (FilterSrcMemory.Length != 0)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(FilterSrcMemory);
|
||||
FilterSrcMemory = Array.Empty<byte>();
|
||||
}
|
||||
if (FilterDstMemory.Length != 0)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(FilterDstMemory);
|
||||
FilterDstMemory = Array.Empty<byte>();
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
private void Init(size_t WinSize, bool Solid)
|
||||
{
|
||||
@@ -150,8 +165,10 @@ internal sealed partial class Unpack : BitInput
|
||||
}
|
||||
}
|
||||
|
||||
//if (Window!=null)
|
||||
// free(Window);
|
||||
if (Window != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(Window);
|
||||
}
|
||||
Window = NewWindow;
|
||||
}
|
||||
|
||||
@@ -290,7 +307,7 @@ internal sealed partial class Unpack : BitInput
|
||||
Dec.MaxNum = Size;
|
||||
|
||||
// Calculate how many entries for every bit length in LengthTable we have.
|
||||
var LengthCount = new uint[16];
|
||||
Span<uint> LengthCount = stackalloc uint[16];
|
||||
//memset(LengthCount,0,sizeof(LengthCount));
|
||||
for (size_t I = 0; I < Size; I++)
|
||||
{
|
||||
@@ -334,9 +351,9 @@ internal sealed partial class Unpack : BitInput
|
||||
|
||||
// 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];
|
||||
Span<uint> CopyDecodePos = stackalloc uint[16];
|
||||
//memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos));
|
||||
Array.Copy(Dec.DecodePos, CopyDecodePos, CopyDecodePos.Length);
|
||||
Dec.DecodePos.AsSpan().CopyTo(CopyDecodePos);
|
||||
|
||||
// For every bit length in the bit length table and so for every item
|
||||
// of alphabet.
|
||||
|
||||
@@ -31,6 +31,10 @@ public abstract partial class RarReader : AbstractReader<RarReaderEntry, RarVolu
|
||||
{
|
||||
unpackV1.Dispose();
|
||||
}
|
||||
if (UnpackV2017.IsValueCreated && UnpackV2017.Value is IDisposable unpackV2017)
|
||||
{
|
||||
unpackV2017.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
base.Dispose();
|
||||
|
||||
Reference in New Issue
Block a user