diff --git a/src/SharpCompress/Compressors/ArcLzw/ArcLzwStream.Async.cs b/src/SharpCompress/Compressors/ArcLzw/ArcLzwStream.Async.cs new file mode 100644 index 00000000..fbcc4572 --- /dev/null +++ b/src/SharpCompress/Compressors/ArcLzw/ArcLzwStream.Async.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using SharpCompress.Compressors.RLE90; + +public partial class ArcLzwStream +{ + public override async Task ReadAsync( + byte[] buffer, + int offset, + int count, + CancellationToken cancellationToken + ) + { + if (_processed) + { + return 0; + } + _processed = true; + var data = new byte[_compressedSize]; + int totalRead = 0; + while (totalRead < _compressedSize) + { + int read = await _stream + .ReadAsync(data, totalRead, _compressedSize - totalRead, cancellationToken) + .ConfigureAwait(false); + if (read == 0) + { + break; + } + totalRead += read; + } + var decoded = Decompress(data, _useCrunched); + var result = decoded.Count(); + if (_useCrunched) + { + var unpacked = RLE.UnpackRLE(decoded.ToArray()); + unpacked.CopyTo(buffer, 0); + result = unpacked.Count; + } + else + { + decoded.CopyTo(buffer, 0); + } + return result; + } + +#if !LEGACY_DOTNET + public override async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) + { + if (buffer.IsEmpty) + { + return 0; + } + + byte[] array = System.Buffers.ArrayPool.Shared.Rent(buffer.Length); + try + { + int read = await ReadAsync(array, 0, buffer.Length, cancellationToken) + .ConfigureAwait(false); + array.AsSpan(0, read).CopyTo(buffer.Span); + return read; + } + finally + { + System.Buffers.ArrayPool.Shared.Return(array); + } + } +#endif +} diff --git a/src/SharpCompress/Compressors/RLE90/RunLength90Stream.Async.cs b/src/SharpCompress/Compressors/RLE90/RunLength90Stream.Async.cs new file mode 100644 index 00000000..84b4a554 --- /dev/null +++ b/src/SharpCompress/Compressors/RLE90/RunLength90Stream.Async.cs @@ -0,0 +1,118 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace SharpCompress.Compressors.RLE90 +{ + public partial class RunLength90Stream + { + public override async Task ReadAsync( + byte[] buffer, + int offset, + int count, + CancellationToken cancellationToken + ) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0 || count < 0 || offset + count > buffer.Length) + { + throw new ArgumentOutOfRangeException(); + } + + int bytesWritten = 0; + + while (bytesWritten < count && !_endOfCompressedData) + { + // Handle pending repeat bytes first + if (_repeatCount > 0) + { + int toWrite = Math.Min(_repeatCount, count - bytesWritten); + for (int i = 0; i < toWrite; i++) + { + buffer[offset + bytesWritten++] = _lastByte; + } + _repeatCount -= toWrite; + continue; + } + + // Try to read the next byte from compressed data + if (_bytesReadFromSource >= _compressedSize) + { + _endOfCompressedData = true; + break; + } + + byte[] singleByte = new byte[1]; + int bytesRead = await _stream + .ReadAsync(singleByte, 0, 1, cancellationToken) + .ConfigureAwait(false); + if (bytesRead == 0) + { + _endOfCompressedData = true; + break; + } + + _bytesReadFromSource++; + byte c = singleByte[0]; + + if (_inDleMode) + { + _inDleMode = false; + + if (c == 0) + { + buffer[offset + bytesWritten++] = DLE; + _lastByte = DLE; + } + else + { + _repeatCount = c - 1; + // We'll handle these repeats in next loop iteration. + } + } + else if (c == DLE) + { + _inDleMode = true; + } + else + { + buffer[offset + bytesWritten++] = c; + _lastByte = c; + } + } + + return bytesWritten; + } + +#if !LEGACY_DOTNET + public override async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) + { + if (buffer.IsEmpty) + { + return 0; + } + + byte[] array = System.Buffers.ArrayPool.Shared.Rent(buffer.Length); + try + { + int read = await ReadAsync(array, 0, buffer.Length, cancellationToken) + .ConfigureAwait(false); + array.AsSpan(0, read).CopyTo(buffer.Span); + return read; + } + finally + { + System.Buffers.ArrayPool.Shared.Return(array); + } + } +#endif + } +} diff --git a/src/SharpCompress/Compressors/RLE90/RunLength90Stream.cs b/src/SharpCompress/Compressors/RLE90/RunLength90Stream.cs index 87d7b957..d1470944 100644 --- a/src/SharpCompress/Compressors/RLE90/RunLength90Stream.cs +++ b/src/SharpCompress/Compressors/RLE90/RunLength90Stream.cs @@ -7,7 +7,7 @@ namespace SharpCompress.Compressors.RLE90 /// Real-time streaming RLE90 decompression stream. /// Decompresses bytes on demand without buffering the entire file in memory. /// - public class RunLength90Stream : Stream + public partial class RunLength90Stream : Stream { private readonly Stream _stream; private readonly int _compressedSize;