mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Make raw LZMA setup and disposal exception-safe
This commit is contained in:
@@ -24,13 +24,20 @@ internal partial class OutWindow : IAsyncDisposable
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await ReleaseStreamAsync().ConfigureAwait(false);
|
||||
if (_buffer is null)
|
||||
try
|
||||
{
|
||||
return;
|
||||
await ReleaseStreamAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
var buffer = _buffer;
|
||||
_buffer = null;
|
||||
_stream = null;
|
||||
if (buffer is not null)
|
||||
{
|
||||
_pool.Return(buffer);
|
||||
}
|
||||
}
|
||||
_pool.Return(_buffer);
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
private async ValueTask FlushAsync(CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -58,13 +58,20 @@ internal partial class OutWindow : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseStream();
|
||||
if (_buffer is null)
|
||||
try
|
||||
{
|
||||
return;
|
||||
ReleaseStream();
|
||||
}
|
||||
finally
|
||||
{
|
||||
var buffer = _buffer;
|
||||
_buffer = null;
|
||||
_stream = null;
|
||||
if (buffer is not null)
|
||||
{
|
||||
_pool.Return(buffer);
|
||||
}
|
||||
}
|
||||
_pool.Return(_buffer);
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
|
||||
@@ -354,7 +354,7 @@ internal sealed class Lzma2EncoderStream : Stream
|
||||
props[3] = (byte)(_dictionarySize >> 16);
|
||||
props[4] = (byte)(_dictionarySize >> 24);
|
||||
|
||||
var decoder = new Decoder();
|
||||
using var decoder = new Decoder();
|
||||
decoder.SetDecoderProperties(props);
|
||||
|
||||
using var input = new MemoryStream(compressedData, 0, compressedSize, writable: false);
|
||||
|
||||
@@ -43,24 +43,32 @@ public partial class LzmaStream
|
||||
isLzma2,
|
||||
leaveOpen
|
||||
);
|
||||
if (!isLzma2)
|
||||
try
|
||||
{
|
||||
if (presetDictionary != null)
|
||||
if (!isLzma2)
|
||||
{
|
||||
await lzma._outWindow.TrainAsync(presetDictionary).ConfigureAwait(false);
|
||||
}
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
await lzma._outWindow.TrainAsync(presetDictionary).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await lzma._rangeDecoder.InitAsync(inputStream).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
await lzma._outWindow.TrainAsync(presetDictionary).ConfigureAwait(false);
|
||||
lzma._needDictReset = false;
|
||||
await lzma._rangeDecoder.InitAsync(inputStream).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
await lzma._outWindow.TrainAsync(presetDictionary).ConfigureAwait(false);
|
||||
lzma._needDictReset = false;
|
||||
}
|
||||
}
|
||||
return lzma;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await lzma.ReleaseBuffersAsync().ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
return lzma;
|
||||
}
|
||||
|
||||
/*public static async ValueTask<LzmaStream> CreateAsync(
|
||||
@@ -175,6 +183,7 @@ public partial class LzmaStream
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (_endReached)
|
||||
{
|
||||
return 0;
|
||||
@@ -289,6 +298,7 @@ public partial class LzmaStream
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (_endReached)
|
||||
{
|
||||
return 0;
|
||||
@@ -407,6 +417,7 @@ public partial class LzmaStream
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (_encoder != null)
|
||||
{
|
||||
@@ -422,6 +433,7 @@ public partial class LzmaStream
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (_encoder != null)
|
||||
{
|
||||
@@ -443,30 +455,59 @@ public partial class LzmaStream
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
|
||||
if (_encoder != null)
|
||||
try
|
||||
{
|
||||
_position = await _encoder.CodeAsync(null, true).ConfigureAwait(false);
|
||||
_encoder.Dispose();
|
||||
}
|
||||
|
||||
if (!_leaveOpen)
|
||||
{
|
||||
if (_inputStream is IAsyncDisposable asyncDisposableInputStream)
|
||||
if (_encoder != null)
|
||||
{
|
||||
await asyncDisposableInputStream.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputStream?.Dispose();
|
||||
_position = await _encoder.CodeAsync(null, true).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
await ReleaseBuffersAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!_leaveOpen)
|
||||
{
|
||||
if (_inputStream is IAsyncDisposable asyncDisposableInputStream)
|
||||
{
|
||||
await asyncDisposableInputStream.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputStream?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await _outWindow.DisposeAsync().ConfigureAwait(false);
|
||||
ReturnAsyncHeaderBuffer();
|
||||
|
||||
#if !LEGACY_DOTNET || NETSTANDARD2_1
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
#endif
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private async ValueTask ReleaseBuffersAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
#pragma warning disable VSTHRD103 // Encoder.Dispose only returns buffers; finalization is separate.
|
||||
_encoder?.Dispose();
|
||||
#pragma warning restore VSTHRD103
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
await _outWindow.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReturnAsyncHeaderBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,24 +54,23 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
if (!isLzma2)
|
||||
{
|
||||
_dictionarySize = BinaryPrimitives.ReadInt32LittleEndian(properties.AsSpan(1));
|
||||
_outWindow.Create(_dictionarySize);
|
||||
|
||||
_decoder = new Decoder();
|
||||
_decoder.SetDecoderProperties(properties);
|
||||
Properties = properties;
|
||||
|
||||
_availableBytes = outputSize < 0 ? long.MaxValue : outputSize;
|
||||
_rangeDecoderLimit = inputSize;
|
||||
// Validate decoder properties before acquiring the dictionary buffer.
|
||||
_outWindow.Create(_dictionarySize);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dictionarySize = 2 | (properties[0] & 1);
|
||||
_dictionarySize <<= (properties[0] >> 1) + 11;
|
||||
|
||||
_outWindow.Create(_dictionarySize);
|
||||
|
||||
Properties = new byte[1];
|
||||
_availableBytes = 0;
|
||||
_outWindow.Create(_dictionarySize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,24 +122,32 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
isLzma2,
|
||||
leaveOpen
|
||||
);
|
||||
if (!isLzma2)
|
||||
try
|
||||
{
|
||||
if (presetDictionary != null)
|
||||
if (!isLzma2)
|
||||
{
|
||||
lzma._outWindow.Train(presetDictionary);
|
||||
}
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
lzma._outWindow.Train(presetDictionary);
|
||||
}
|
||||
|
||||
lzma._rangeDecoder.Init(inputStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
lzma._outWindow.Train(presetDictionary);
|
||||
lzma._needDictReset = false;
|
||||
lzma._rangeDecoder.Init(inputStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
lzma._outWindow.Train(presetDictionary);
|
||||
lzma._needDictReset = false;
|
||||
}
|
||||
}
|
||||
return lzma;
|
||||
}
|
||||
catch
|
||||
{
|
||||
lzma.ReleaseBuffers();
|
||||
throw;
|
||||
}
|
||||
return lzma;
|
||||
}
|
||||
|
||||
private LzmaStream(LzmaEncoderProperties properties, bool isLzma2)
|
||||
@@ -175,21 +182,28 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
)
|
||||
{
|
||||
var lzma = new LzmaStream(properties, isLzma2);
|
||||
|
||||
lzma._encoder!.SetStreams(null, outputStream, -1, -1);
|
||||
|
||||
if (presetDictionary != null)
|
||||
try
|
||||
{
|
||||
lzma._encoder.Train(presetDictionary);
|
||||
lzma._encoder!.SetStreams(null, outputStream, -1, -1);
|
||||
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
lzma._encoder.Train(presetDictionary);
|
||||
}
|
||||
return lzma;
|
||||
}
|
||||
catch
|
||||
{
|
||||
lzma.ReleaseBuffers();
|
||||
throw;
|
||||
}
|
||||
return lzma;
|
||||
}
|
||||
|
||||
public override bool CanRead => _encoder == null;
|
||||
public override bool CanRead => !_isDisposed && _encoder == null;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => _encoder != null;
|
||||
public override bool CanWrite => !_isDisposed && _encoder != null;
|
||||
|
||||
public override void Flush() { }
|
||||
|
||||
@@ -202,21 +216,58 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
if (disposing)
|
||||
try
|
||||
{
|
||||
if (_encoder != null)
|
||||
if (disposing && _encoder != null)
|
||||
{
|
||||
_position = _encoder.Code(null, true);
|
||||
_encoder.Dispose();
|
||||
}
|
||||
if (!_leaveOpen)
|
||||
{
|
||||
_inputStream?.Dispose();
|
||||
}
|
||||
_outWindow.Dispose();
|
||||
ReturnAsyncHeaderBuffer();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
finally
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReleaseBuffers();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!_leaveOpen)
|
||||
{
|
||||
_inputStream?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseBuffers()
|
||||
{
|
||||
try
|
||||
{
|
||||
_encoder?.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
_outWindow.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReturnAsyncHeaderBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(LzmaStream));
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] GetAsyncHeaderBuffer() => _asyncHeaderBuffer ??= ArrayPool<byte>.Shared.Rent(6);
|
||||
@@ -242,6 +293,7 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (_endReached)
|
||||
{
|
||||
return 0;
|
||||
@@ -337,6 +389,7 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
|
||||
public override int ReadByte()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (_endReached)
|
||||
{
|
||||
return -1;
|
||||
@@ -506,6 +559,7 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (_encoder != null)
|
||||
{
|
||||
_position = _encoder.Code(new MemoryStream(buffer, offset, count), false);
|
||||
|
||||
Reference in New Issue
Block a user