diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs index c65cc683..6e35b232 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs @@ -153,7 +153,7 @@ internal class OutWindow : IDisposable _pendingLen = rem; } - public async Task CopyPendingAsync(CancellationToken cancellationToken = default) + public async ValueTask CopyPendingAsync(CancellationToken cancellationToken = default) { if (_pendingLen < 1) { @@ -206,7 +206,7 @@ internal class OutWindow : IDisposable _pendingDist = distance; } - public async Task CopyBlockAsync( + public async ValueTask CopyBlockAsync( int distance, int len, CancellationToken cancellationToken = default @@ -253,7 +253,7 @@ internal class OutWindow : IDisposable } } - public async Task PutByteAsync(byte b, CancellationToken cancellationToken = default) + public async ValueTask PutByteAsync(byte b, CancellationToken cancellationToken = default) { _buffer[_pos++] = b; _total++; @@ -369,6 +369,28 @@ internal class OutWindow : IDisposable return size; } + public int Read(Memory buffer, int offset, int count) + { + if (_streamPos >= _pos) + { + return 0; + } + + var size = _pos - _streamPos; + if (size > count) + { + size = count; + } + _buffer.AsMemory(_streamPos, size).CopyTo(buffer.Slice(offset, size)); + _streamPos += size; + if (_streamPos >= _windowSize) + { + _pos = 0; + _streamPos = 0; + } + return size; + } + public int ReadByte() { if (_streamPos >= _pos) diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index fed40533..95d3d027 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Threading.Tasks; using SharpCompress.Compressors.LZMA.LZ; using SharpCompress.Compressors.LZMA.RangeCoder; @@ -475,7 +476,7 @@ public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream return false; } - internal async System.Threading.Tasks.Task CodeAsync( + internal async ValueTask CodeAsync( int dictionarySize, OutWindow outWindow, RangeCoder.Decoder rangeDecoder, diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs index e9d5877f..e63a71ce 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs @@ -425,7 +425,7 @@ public class LzmaStream : Stream, IStreamStack } } - private async Task DecodeChunkHeaderAsync(CancellationToken cancellationToken = default) + private async ValueTask DecodeChunkHeaderAsync(CancellationToken cancellationToken = default) { var controlBuffer = new byte[1]; await _inputStream @@ -632,6 +632,120 @@ public class LzmaStream : Stream, IStreamStack return total; } + +#if !NETFRAMEWORK && !NETSTANDARD2_0 + public override async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) + { + if (_endReached) + { + return 0; + } + + var total = 0; + var offset = 0; + var count = buffer.Length; + while (total < count) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (_availableBytes == 0) + { + if (_isLzma2) + { + await DecodeChunkHeaderAsync(cancellationToken).ConfigureAwait(false); + } + else + { + _endReached = true; + } + if (_endReached) + { + break; + } + } + + var toProcess = count - total; + if (toProcess > _availableBytes) + { + toProcess = (int)_availableBytes; + } + + _outWindow.SetLimit(toProcess); + if (_uncompressedChunk) + { + _inputPosition += await _outWindow + .CopyStreamAsync(_inputStream, toProcess, cancellationToken) + .ConfigureAwait(false); + } + else if ( + await _decoder + .CodeAsync(_dictionarySize, _outWindow, _rangeDecoder, cancellationToken) + .ConfigureAwait(false) + && _outputSize < 0 + ) + { + _availableBytes = _outWindow.AvailableBytes; + } + + var read = _outWindow.Read(buffer, offset, toProcess); + total += read; + offset += read; + _position += read; + _availableBytes -= read; + + if (_availableBytes == 0 && !_uncompressedChunk) + { + if ( + !_rangeDecoder.IsFinished + || (_rangeDecoderLimit >= 0 && _rangeDecoder._total != _rangeDecoderLimit) + ) + { + _outWindow.SetLimit(toProcess + 1); + if ( + !await _decoder + .CodeAsync( + _dictionarySize, + _outWindow, + _rangeDecoder, + cancellationToken + ) + .ConfigureAwait(false) + ) + { + _rangeDecoder.ReleaseStream(); + throw new DataErrorException(); + } + } + + _rangeDecoder.ReleaseStream(); + + _inputPosition += _rangeDecoder._total; + if (_outWindow.HasPending) + { + throw new DataErrorException(); + } + } + } + + if (_endReached) + { + if (_inputSize >= 0 && _inputPosition != _inputSize) + { + throw new DataErrorException(); + } + if (_outputSize >= 0 && _position != _outputSize) + { + throw new DataErrorException(); + } + } + + return total; + } +#endif + public override Task WriteAsync( byte[] buffer, int offset, diff --git a/src/SharpCompress/IO/BufferedSubStream.cs b/src/SharpCompress/IO/BufferedSubStream.cs index 6725196b..f9216216 100755 --- a/src/SharpCompress/IO/BufferedSubStream.cs +++ b/src/SharpCompress/IO/BufferedSubStream.cs @@ -70,7 +70,7 @@ internal class BufferedSubStream : SharpCompressStream, IStreamStack BytesLeftToRead -= _cacheLength; } - private async Task RefillCacheAsync(CancellationToken cancellationToken) + private async ValueTask RefillCacheAsync(CancellationToken cancellationToken) { var count = (int)Math.Min(BytesLeftToRead, _cache.Length); _cacheOffset = 0; diff --git a/src/SharpCompress/IO/ReadOnlySubStream.cs b/src/SharpCompress/IO/ReadOnlySubStream.cs index 86ef1645..6fbf6e41 100644 --- a/src/SharpCompress/IO/ReadOnlySubStream.cs +++ b/src/SharpCompress/IO/ReadOnlySubStream.cs @@ -124,8 +124,13 @@ internal class ReadOnlySubStream : SharpCompressStream, IStreamStack ) { var sliceLen = BytesLeftToRead < buffer.Length ? BytesLeftToRead : buffer.Length; + var mem = buffer; + if (sliceLen != buffer.Length) + { + mem = buffer.Slice(0, (int)sliceLen); + } var read = await Stream - .ReadAsync(buffer.Slice(0, (int)sliceLen), cancellationToken) + .ReadAsync(mem, cancellationToken) .ConfigureAwait(false); if (read > 0) {