mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-08 18:16:30 +00:00
Reduce some allocations by pooling or better disposal
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA.LZ;
|
||||
@@ -108,7 +109,12 @@ internal sealed class BinTree : InWindow
|
||||
var cyclicBufferSize = historySize + 1;
|
||||
if (_cyclicBufferSize != cyclicBufferSize)
|
||||
{
|
||||
_son = new uint[(_cyclicBufferSize = cyclicBufferSize) * 2];
|
||||
if (_son is not null)
|
||||
{
|
||||
ArrayPool<uint>.Shared.Return(_son);
|
||||
}
|
||||
_cyclicBufferSize = cyclicBufferSize;
|
||||
_son = ArrayPool<uint>.Shared.Rent(checked((int)(_cyclicBufferSize * 2)));
|
||||
}
|
||||
|
||||
var hs = K_BT2_HASH_SIZE;
|
||||
@@ -132,7 +138,27 @@ internal sealed class BinTree : InWindow
|
||||
}
|
||||
if (hs != _hashSizeSum)
|
||||
{
|
||||
_hash = new uint[_hashSizeSum = hs];
|
||||
if (_hash is not null)
|
||||
{
|
||||
ArrayPool<uint>.Shared.Return(_hash);
|
||||
}
|
||||
_hashSizeSum = hs;
|
||||
_hash = ArrayPool<uint>.Shared.Rent(checked((int)_hashSizeSum));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
if (_son is not null)
|
||||
{
|
||||
ArrayPool<uint>.Shared.Return(_son);
|
||||
_son = null;
|
||||
}
|
||||
if (_hash is not null)
|
||||
{
|
||||
ArrayPool<uint>.Shared.Return(_hash);
|
||||
_hash = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA.LZ;
|
||||
|
||||
internal class InWindow
|
||||
internal class InWindow : IDisposable
|
||||
{
|
||||
public byte[] _bufferBase; // pointer to buffer with data
|
||||
private Stream _stream;
|
||||
@@ -78,7 +80,22 @@ internal class InWindow
|
||||
}
|
||||
}
|
||||
|
||||
private void Free() => _bufferBase = null;
|
||||
private void Free()
|
||||
{
|
||||
if (_bufferBase is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayPool<byte>.Shared.Return(_bufferBase);
|
||||
_bufferBase = null;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
ReleaseStream();
|
||||
Free();
|
||||
}
|
||||
|
||||
public void Create(uint keepSizeBefore, uint keepSizeAfter, uint keepSizeReserv)
|
||||
{
|
||||
@@ -89,7 +106,7 @@ internal class InWindow
|
||||
{
|
||||
Free();
|
||||
_blockSize = blockSize;
|
||||
_bufferBase = new byte[_blockSize];
|
||||
_bufferBase = ArrayPool<byte>.Shared.Rent(checked((int)_blockSize));
|
||||
}
|
||||
_pointerToLastSafePosition = _blockSize - keepSizeAfter;
|
||||
_streamEndWasReached = false;
|
||||
|
||||
@@ -126,31 +126,36 @@ public sealed partial class LZipStream
|
||||
)
|
||||
{
|
||||
// Read the header
|
||||
byte[] header = new byte[6];
|
||||
var n = await stream
|
||||
.ReadAsync(header, 0, header.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// TODO: Handle reading only part of the header?
|
||||
|
||||
if (n != 6)
|
||||
var header = ArrayPool<byte>.Shared.Rent(6);
|
||||
try
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var n = await stream.ReadAsync(header, 0, 6, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (
|
||||
header[0] != 'L'
|
||||
|| header[1] != 'Z'
|
||||
|| header[2] != 'I'
|
||||
|| header[3] != 'P'
|
||||
|| header[4] != 1 /* version 1 */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
// TODO: Handle reading only part of the header?
|
||||
|
||||
if (n != 6)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (
|
||||
header[0] != 'L'
|
||||
|| header[1] != 'Z'
|
||||
|| header[2] != 'I'
|
||||
|| header[3] != 'P'
|
||||
|| header[4] != 1 /* version 1 */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var basePower = header[5] & 0x1F;
|
||||
var subtractionNumerator = (header[5] & 0xE0) >> 5;
|
||||
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(header);
|
||||
}
|
||||
var basePower = header[5] & 0x1F;
|
||||
var subtractionNumerator = (header[5] & 0xE0) >> 5;
|
||||
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -26,11 +27,12 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
private readonly int _dictionarySize;
|
||||
private readonly int _numFastBytes;
|
||||
private readonly byte[] _buffer;
|
||||
private readonly byte[] _properties;
|
||||
private readonly Encoder _encoder;
|
||||
private int _bufferPosition;
|
||||
private bool _isFirstChunk = true;
|
||||
private bool _isDisposed;
|
||||
private byte _lzmaPropertiesByte;
|
||||
private bool _lzmaPropertiesKnown;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new LZMA2 encoder stream.
|
||||
@@ -43,14 +45,23 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
_output = output;
|
||||
_dictionarySize = dictionarySize;
|
||||
_numFastBytes = numFastBytes;
|
||||
_buffer = new byte[MAX_UNCOMPRESSED_CHUNK_SIZE];
|
||||
_buffer = ArrayPool<byte>.Shared.Rent(MAX_UNCOMPRESSED_CHUNK_SIZE);
|
||||
_bufferPosition = 0;
|
||||
|
||||
var encoderProps = new LzmaEncoderProperties(eos: false, _dictionarySize, _numFastBytes);
|
||||
_encoder = new Encoder();
|
||||
_encoder.SetCoderProperties(encoderProps.PropIDs, encoderProps.Properties);
|
||||
|
||||
Span<byte> lzmaProperties = stackalloc byte[5];
|
||||
_encoder.WriteCoderProperties(lzmaProperties);
|
||||
_lzmaPropertiesByte = lzmaProperties[0];
|
||||
_properties = [EncodeDictionarySize(_dictionarySize)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the 1-byte LZMA2 properties (encoded dictionary size).
|
||||
/// </summary>
|
||||
public byte[] Properties => [EncodeDictionarySize(_dictionarySize)];
|
||||
public byte[] Properties => _properties;
|
||||
|
||||
public override bool CanRead => false;
|
||||
public override bool CanSeek => false;
|
||||
@@ -153,6 +164,8 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
|
||||
// Write LZMA2 end marker
|
||||
_output.WriteByte(0x00);
|
||||
_encoder.Dispose();
|
||||
ArrayPool<byte>.Shared.Return(_buffer);
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -179,6 +192,9 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
#else
|
||||
await _output.WriteAsync(_endMarker, 0, 1).ConfigureAwait(false);
|
||||
#endif
|
||||
|
||||
_encoder.Dispose();
|
||||
ArrayPool<byte>.Shared.Return(_buffer);
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET || NETSTANDARD2_1
|
||||
@@ -193,14 +209,15 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
return;
|
||||
}
|
||||
|
||||
var uncompressedData = _buffer.AsSpan(0, _bufferPosition);
|
||||
var uncompressedSize = _bufferPosition;
|
||||
var uncompressedData = _buffer.AsSpan(0, uncompressedSize);
|
||||
_bufferPosition = 0;
|
||||
|
||||
// Try compressing the data
|
||||
byte[] compressed;
|
||||
(byte[] Buffer, int Length) compressed;
|
||||
try
|
||||
{
|
||||
compressed = CompressBlock(uncompressedData);
|
||||
compressed = CompressBlock(_buffer, uncompressedSize);
|
||||
}
|
||||
catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException)
|
||||
{
|
||||
@@ -215,7 +232,7 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
&& compressed.Length < uncompressedData.Length
|
||||
)
|
||||
{
|
||||
WriteCompressedChunk(uncompressedData.Length, compressed);
|
||||
WriteCompressedChunk(uncompressedData.Length, compressed.Buffer, compressed.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -230,13 +247,14 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
return;
|
||||
}
|
||||
|
||||
var uncompressedData = _buffer.AsMemory(0, _bufferPosition);
|
||||
var uncompressedSize = _bufferPosition;
|
||||
var uncompressedData = _buffer.AsMemory(0, uncompressedSize);
|
||||
_bufferPosition = 0;
|
||||
|
||||
byte[] compressed;
|
||||
(byte[] Buffer, int Length) compressed;
|
||||
try
|
||||
{
|
||||
compressed = CompressBlock(uncompressedData.Span);
|
||||
compressed = CompressBlock(_buffer, uncompressedSize);
|
||||
}
|
||||
catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException)
|
||||
{
|
||||
@@ -250,7 +268,12 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
&& compressed.Length < uncompressedData.Length
|
||||
)
|
||||
{
|
||||
await WriteCompressedChunkAsync(uncompressedData.Length, compressed, cancellationToken)
|
||||
await WriteCompressedChunkAsync(
|
||||
uncompressedData.Length,
|
||||
compressed.Buffer,
|
||||
compressed.Length,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
@@ -260,45 +283,26 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] CompressBlock(ReadOnlySpan<byte> data)
|
||||
private (byte[] Buffer, int Length) CompressBlock(byte[] data, int length)
|
||||
{
|
||||
var encoderProps = new LzmaEncoderProperties(eos: false, _dictionarySize, _numFastBytes);
|
||||
|
||||
var encoder = new Encoder();
|
||||
encoder.SetCoderProperties(encoderProps.PropIDs, encoderProps.Properties);
|
||||
|
||||
// Capture the LZMA properties byte (pb/lp/lc encoding) for the chunk header
|
||||
if (!_lzmaPropertiesKnown)
|
||||
{
|
||||
var propBytes = new byte[5];
|
||||
encoder.WriteCoderProperties(propBytes);
|
||||
_lzmaPropertiesByte = propBytes[0];
|
||||
_lzmaPropertiesKnown = true;
|
||||
}
|
||||
|
||||
using var inputMs = new MemoryStream(data.ToArray(), writable: false);
|
||||
using var inputMs = new MemoryStream(data, 0, length, writable: false);
|
||||
using var outputMs = new PooledMemoryStream();
|
||||
|
||||
encoder.Code(inputMs, outputMs, data.Length, -1, null);
|
||||
_encoder.Code(inputMs, outputMs, length, -1, null);
|
||||
|
||||
var fullCompressed = outputMs.ToArray();
|
||||
|
||||
// The LZMA range encoder flush writes trailing bytes the decoder doesn't consume.
|
||||
// Trial-decode to find the exact byte count the decoder needs, so the LZMA2
|
||||
// chunk header reports a compressed size that matches what the decoder reads.
|
||||
var consumed = FindConsumedBytes(fullCompressed, data.Length);
|
||||
if (consumed < fullCompressed.Length)
|
||||
{
|
||||
return fullCompressed.AsSpan(0, consumed).ToArray();
|
||||
}
|
||||
|
||||
return fullCompressed;
|
||||
var consumed = FindConsumedBytes(fullCompressed, fullCompressed.Length, length);
|
||||
return (fullCompressed, consumed);
|
||||
}
|
||||
|
||||
private int FindConsumedBytes(byte[] compressedData, int uncompressedSize)
|
||||
private int FindConsumedBytes(byte[] compressedData, int compressedSize, int uncompressedSize)
|
||||
{
|
||||
// Build 5-byte LZMA property header: [pb/lp/lc byte] [dictSize as LE int32]
|
||||
var props = new byte[5];
|
||||
Span<byte> props = stackalloc byte[5];
|
||||
props[0] = _lzmaPropertiesByte;
|
||||
props[1] = (byte)_dictionarySize;
|
||||
props[2] = (byte)(_dictionarySize >> 8);
|
||||
@@ -308,9 +312,8 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
var decoder = new Decoder();
|
||||
decoder.SetDecoderProperties(props);
|
||||
|
||||
using var input = new MemoryStream(compressedData);
|
||||
using var output = new PooledMemoryStream();
|
||||
decoder.Code(input, output, compressedData.Length, uncompressedSize, null);
|
||||
using var input = new MemoryStream(compressedData, 0, compressedSize, writable: false);
|
||||
decoder.Code(input, Stream.Null, compressedSize, uncompressedSize, null);
|
||||
|
||||
return (int)input.Position;
|
||||
}
|
||||
@@ -319,10 +322,14 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
/// Writes a compressed LZMA2 chunk.
|
||||
/// Header: [control] [uncompSize_hi] [uncompSize_lo] [compSize_hi] [compSize_lo] [props?]
|
||||
/// </summary>
|
||||
private void WriteCompressedChunk(int uncompressedSize, byte[] compressedData)
|
||||
private void WriteCompressedChunk(
|
||||
int uncompressedSize,
|
||||
byte[] compressedData,
|
||||
int compressedSize
|
||||
)
|
||||
{
|
||||
var uncompSizeMinus1 = uncompressedSize - 1;
|
||||
var compSizeMinus1 = compressedData.Length - 1;
|
||||
var compSizeMinus1 = compressedSize - 1;
|
||||
|
||||
// Each chunk is compressed independently with a fresh LZMA encoder,
|
||||
// so we must use 0xE0 (full reset: dictionary + state + properties) every time.
|
||||
@@ -341,32 +348,38 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
// 0xE0 (>= 0xC0) requires properties byte
|
||||
_output.WriteByte(_lzmaPropertiesByte);
|
||||
|
||||
_output.Write(compressedData, 0, compressedData.Length);
|
||||
_output.Write(compressedData, 0, compressedSize);
|
||||
}
|
||||
|
||||
private async ValueTask WriteCompressedChunkAsync(
|
||||
int uncompressedSize,
|
||||
byte[] compressedData,
|
||||
int compressedSize,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var uncompSizeMinus1 = uncompressedSize - 1;
|
||||
var compSizeMinus1 = compressedData.Length - 1;
|
||||
var compSizeMinus1 = compressedSize - 1;
|
||||
var control = (byte)(0xE0 | ((uncompSizeMinus1 >> 16) & 0x1F));
|
||||
_isFirstChunk = false;
|
||||
|
||||
var header = new[]
|
||||
var header = ArrayPool<byte>.Shared.Rent(6);
|
||||
try
|
||||
{
|
||||
control,
|
||||
(byte)((uncompSizeMinus1 >> 8) & 0xFF),
|
||||
(byte)(uncompSizeMinus1 & 0xFF),
|
||||
(byte)((compSizeMinus1 >> 8) & 0xFF),
|
||||
(byte)(compSizeMinus1 & 0xFF),
|
||||
_lzmaPropertiesByte,
|
||||
};
|
||||
await _output.WriteAsync(header, 0, header.Length, cancellationToken).ConfigureAwait(false);
|
||||
header[0] = control;
|
||||
header[1] = (byte)((uncompSizeMinus1 >> 8) & 0xFF);
|
||||
header[2] = (byte)(uncompSizeMinus1 & 0xFF);
|
||||
header[3] = (byte)((compSizeMinus1 >> 8) & 0xFF);
|
||||
header[4] = (byte)(compSizeMinus1 & 0xFF);
|
||||
header[5] = _lzmaPropertiesByte;
|
||||
await _output.WriteAsync(header, 0, 6, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(header);
|
||||
}
|
||||
await _output
|
||||
.WriteAsync(compressedData, 0, compressedData.Length, cancellationToken)
|
||||
.WriteAsync(compressedData, 0, compressedSize, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -426,20 +439,37 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
control = 0x02;
|
||||
}
|
||||
|
||||
var header = new[]
|
||||
var header = ArrayPool<byte>.Shared.Rent(3);
|
||||
try
|
||||
{
|
||||
control,
|
||||
(byte)((sizeMinus1 >> 8) & 0xFF),
|
||||
(byte)(sizeMinus1 & 0xFF),
|
||||
};
|
||||
await _output
|
||||
.WriteAsync(header, 0, header.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
header[0] = control;
|
||||
header[1] = (byte)((sizeMinus1 >> 8) & 0xFF);
|
||||
header[2] = (byte)(sizeMinus1 & 0xFF);
|
||||
await _output.WriteAsync(header, 0, 3, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(header);
|
||||
}
|
||||
|
||||
var chunk = data.Slice(offset, chunkSize).ToArray();
|
||||
#if !LEGACY_DOTNET || NETSTANDARD2_1
|
||||
await _output
|
||||
.WriteAsync(chunk, 0, chunk.Length, cancellationToken)
|
||||
.WriteAsync(data.Slice(offset, chunkSize), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
#else
|
||||
var chunk = ArrayPool<byte>.Shared.Rent(chunkSize);
|
||||
try
|
||||
{
|
||||
data.Slice(offset, chunkSize).CopyTo(chunk);
|
||||
await _output
|
||||
.WriteAsync(chunk, 0, chunkSize, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(chunk);
|
||||
}
|
||||
#endif
|
||||
offset += chunkSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,7 +446,10 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetDecoderProperties(byte[] properties)
|
||||
public void SetDecoderProperties(byte[] properties) =>
|
||||
SetDecoderProperties(properties.AsSpan());
|
||||
|
||||
internal void SetDecoderProperties(ReadOnlySpan<byte> properties)
|
||||
{
|
||||
if (properties.Length < 1)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ using SharpCompress.Compressors.LZMA.RangeCoder;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA;
|
||||
|
||||
internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties
|
||||
internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties, IDisposable
|
||||
{
|
||||
private enum EMatchFinderType
|
||||
{
|
||||
@@ -574,6 +574,12 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_matchFinder?.Dispose();
|
||||
_matchFinder = null;
|
||||
}
|
||||
|
||||
private void SetWriteEndMarkerMode(bool writeEndMarker) => _writeEndMark = writeEndMarker;
|
||||
|
||||
private void Init()
|
||||
|
||||
@@ -83,11 +83,11 @@ public partial class LzmaStream
|
||||
|
||||
private async ValueTask DecodeChunkHeaderAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var controlBuffer = new byte[1];
|
||||
var headerBuffer = GetAsyncHeaderBuffer();
|
||||
await _inputStream!
|
||||
.ReadExactAsync(controlBuffer, 0, 1, cancellationToken)
|
||||
.ReadExactAsync(headerBuffer, 0, 1, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var control = controlBuffer[0];
|
||||
var control = headerBuffer[0];
|
||||
_inputPosition++;
|
||||
|
||||
if (control == 0x00)
|
||||
@@ -112,26 +112,25 @@ public partial class LzmaStream
|
||||
_uncompressedChunk = false;
|
||||
|
||||
_availableBytes = (control & 0x1F) << 16;
|
||||
var buffer = new byte[2];
|
||||
await _inputStream!
|
||||
.ReadExactAsync(buffer, 0, 2, cancellationToken)
|
||||
.ReadExactAsync(headerBuffer, 0, 2, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
_availableBytes += (buffer[0] << 8) + buffer[1] + 1;
|
||||
_availableBytes += (headerBuffer[0] << 8) + headerBuffer[1] + 1;
|
||||
_inputPosition += 2;
|
||||
|
||||
await _inputStream!
|
||||
.ReadExactAsync(buffer, 0, 2, cancellationToken)
|
||||
.ReadExactAsync(headerBuffer, 0, 2, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
_rangeDecoderLimit = (buffer[0] << 8) + buffer[1] + 1;
|
||||
_rangeDecoderLimit = (headerBuffer[0] << 8) + headerBuffer[1] + 1;
|
||||
_inputPosition += 2;
|
||||
|
||||
if (control >= 0xC0)
|
||||
{
|
||||
_needProps = false;
|
||||
await _inputStream!
|
||||
.ReadExactAsync(controlBuffer, 0, 1, cancellationToken)
|
||||
.ReadExactAsync(headerBuffer, 0, 1, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
Properties[0] = controlBuffer[0];
|
||||
Properties[0] = headerBuffer[0];
|
||||
_inputPosition++;
|
||||
|
||||
_decoder = new Decoder();
|
||||
@@ -156,11 +155,10 @@ public partial class LzmaStream
|
||||
else
|
||||
{
|
||||
_uncompressedChunk = true;
|
||||
var buffer = new byte[2];
|
||||
await _inputStream!
|
||||
.ReadExactAsync(buffer, 0, 2, cancellationToken)
|
||||
.ReadExactAsync(headerBuffer, 0, 2, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
_availableBytes = (buffer[0] << 8) + buffer[1] + 1;
|
||||
_availableBytes = (headerBuffer[0] << 8) + headerBuffer[1] + 1;
|
||||
_inputPosition += 2;
|
||||
}
|
||||
}
|
||||
@@ -436,6 +434,7 @@ public partial class LzmaStream
|
||||
if (_encoder != null)
|
||||
{
|
||||
_position = await _encoder.CodeAsync(null, true).ConfigureAwait(false);
|
||||
_encoder.Dispose();
|
||||
}
|
||||
|
||||
if (!_leaveOpen)
|
||||
@@ -450,6 +449,7 @@ public partial class LzmaStream
|
||||
}
|
||||
}
|
||||
await _outWindow.DisposeAsync().ConfigureAwait(false);
|
||||
ReturnAsyncHeaderBuffer();
|
||||
|
||||
#if !LEGACY_DOTNET || NETSTANDARD2_1
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
@@ -33,6 +34,7 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
private bool _needProps = true;
|
||||
|
||||
private readonly Encoder? _encoder;
|
||||
private byte[]? _asyncHeaderBuffer;
|
||||
private bool _isDisposed;
|
||||
|
||||
private LzmaStream(
|
||||
@@ -205,16 +207,31 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
if (_encoder != null)
|
||||
{
|
||||
_position = _encoder.Code(null, true);
|
||||
_encoder.Dispose();
|
||||
}
|
||||
if (!_leaveOpen)
|
||||
{
|
||||
_inputStream?.Dispose();
|
||||
}
|
||||
_outWindow.Dispose();
|
||||
ReturnAsyncHeaderBuffer();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private byte[] GetAsyncHeaderBuffer() => _asyncHeaderBuffer ??= ArrayPool<byte>.Shared.Rent(6);
|
||||
|
||||
private void ReturnAsyncHeaderBuffer()
|
||||
{
|
||||
if (_asyncHeaderBuffer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayPool<byte>.Shared.Return(_asyncHeaderBuffer);
|
||||
_asyncHeaderBuffer = null;
|
||||
}
|
||||
|
||||
public override long Length => _position + _availableBytes;
|
||||
|
||||
public override long Position
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder;
|
||||
|
||||
internal partial class Encoder
|
||||
{
|
||||
private byte[] SingleByteBuffer => _singleByteBuffer ??= new byte[1];
|
||||
|
||||
public async ValueTask ShiftLowAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if ((uint)_low < 0xFF000000 || (uint)(_low >> 32) == 1)
|
||||
@@ -17,7 +19,8 @@ internal partial class Encoder
|
||||
do
|
||||
{
|
||||
var b = (byte)(temp + (_low >> 32));
|
||||
var buffer = new[] { b };
|
||||
var buffer = SingleByteBuffer;
|
||||
buffer[0] = b;
|
||||
await _stream.WriteAsync(buffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
temp = 0xFF;
|
||||
} while (--_cacheSize != 0);
|
||||
@@ -78,13 +81,15 @@ internal partial class Encoder
|
||||
|
||||
internal partial class Decoder
|
||||
{
|
||||
private byte[] SingleByteBuffer => _singleByteBuffer ??= new byte[1];
|
||||
|
||||
public async ValueTask InitAsync(Stream stream, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_stream = stream;
|
||||
|
||||
_code = 0;
|
||||
_range = 0xFFFFFFFF;
|
||||
var buffer = new byte[1];
|
||||
var buffer = SingleByteBuffer;
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var read = await _stream
|
||||
@@ -103,7 +108,7 @@ internal partial class Decoder
|
||||
{
|
||||
while (_range < K_TOP_VALUE)
|
||||
{
|
||||
var buffer = new byte[1];
|
||||
var buffer = SingleByteBuffer;
|
||||
var read = await _stream
|
||||
.ReadAsync(buffer, 0, 1, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
@@ -121,7 +126,7 @@ internal partial class Decoder
|
||||
{
|
||||
if (_range < K_TOP_VALUE)
|
||||
{
|
||||
var buffer = new byte[1];
|
||||
var buffer = SingleByteBuffer;
|
||||
var read = await _stream
|
||||
.ReadAsync(buffer, 0, 1, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
@@ -143,7 +148,7 @@ internal partial class Decoder
|
||||
var range = _range;
|
||||
var code = _code;
|
||||
uint result = 0;
|
||||
var buffer = new byte[1];
|
||||
var buffer = SingleByteBuffer;
|
||||
for (var i = numTotalBits; i > 0; i--)
|
||||
{
|
||||
range >>= 1;
|
||||
|
||||
@@ -15,8 +15,7 @@ internal partial class Encoder
|
||||
public uint _range;
|
||||
private uint _cacheSize;
|
||||
private byte _cache;
|
||||
|
||||
//long StartPosition;
|
||||
private byte[] _singleByteBuffer;
|
||||
|
||||
public void SetStream(Stream stream) => _stream = stream;
|
||||
|
||||
@@ -88,6 +87,7 @@ internal partial class Decoder
|
||||
|
||||
public Stream _stream;
|
||||
public long _total;
|
||||
private byte[] _singleByteBuffer;
|
||||
|
||||
public void Init(Stream stream)
|
||||
{
|
||||
@@ -180,6 +180,4 @@ internal partial class Decoder
|
||||
}
|
||||
|
||||
public bool IsFinished => _code == 0;
|
||||
|
||||
// ulong GetProcessedSize() {return Stream.GetProcessedSize(); }
|
||||
}
|
||||
|
||||
@@ -286,9 +286,9 @@
|
||||
"net10.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.8, )",
|
||||
"resolved": "10.0.8",
|
||||
"contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw=="
|
||||
"requested": "[10.0.6, )",
|
||||
"resolved": "10.0.6",
|
||||
"contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
@@ -388,9 +388,9 @@
|
||||
"net8.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.27, )",
|
||||
"resolved": "8.0.27",
|
||||
"contentHash": "rQi9TxifHRnXP7lVRZH05DxD2/XGbJp12q0ozcbrlBlBnyyzssFTH/2vLhtKWUp2CT1qVscTrcYTFiwTyKPKRg=="
|
||||
"requested": "[8.0.26, )",
|
||||
"resolved": "8.0.26",
|
||||
"contentHash": "o7/yVssM2r9Wyln2s9edBd5ANZXqdSdBI+g7JqXkyJmXrhs2WsJp25K5yPnYrTgdKBCjKB8bg+O2oew4sgzFaA=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
|
||||
Reference in New Issue
Block a user