diff --git a/src/SharpCompress/Common/AsyncBinaryReader.cs b/src/SharpCompress/Common/AsyncBinaryReader.cs index 2600f319..a6a7bb9c 100644 --- a/src/SharpCompress/Common/AsyncBinaryReader.cs +++ b/src/SharpCompress/Common/AsyncBinaryReader.cs @@ -6,12 +6,31 @@ using System.Threading.Tasks; namespace SharpCompress.Common { - public sealed class AsyncBinaryReader(Stream stream, bool leaveOpen = false) : IDisposable + public sealed class AsyncBinaryReader : IDisposable { - private readonly Stream _stream = stream ?? throw new ArgumentNullException(nameof(stream)); + private readonly Stream _stream; + private readonly Stream _originalStream; + private readonly bool _leaveOpen; private readonly byte[] _buffer = new byte[8]; private bool _disposed; + public AsyncBinaryReader(Stream stream, bool leaveOpen = false, int bufferSize = 4096) + { + _originalStream = stream ?? throw new ArgumentNullException(nameof(stream)); + _leaveOpen = leaveOpen; + + // Wrap the stream with BufferedStream if it's not already a buffered stream + // This enables efficient async reading with internal buffering + if (stream is BufferedStream || stream is IO.SharpCompressStream) + { + _stream = stream; + } + else + { + _stream = new BufferedStream(stream, bufferSize); + } + } + public Stream BaseStream => _stream; public async ValueTask ReadByteAsync(CancellationToken ct = default) @@ -31,6 +50,7 @@ namespace SharpCompress.Common await ReadExactAsync(_buffer, 0, 4, ct).ConfigureAwait(false); return BinaryPrimitives.ReadUInt32LittleEndian(_buffer); } + public async ValueTask ReadUInt64Async(CancellationToken ct = default) { await ReadExactAsync(_buffer, 0, 8, ct).ConfigureAwait(false); @@ -44,12 +64,19 @@ namespace SharpCompress.Common return result; } - private async ValueTask ReadExactAsync(byte[] destination, int offset, int length, CancellationToken ct) + private async ValueTask ReadExactAsync( + byte[] destination, + int offset, + int length, + CancellationToken ct + ) { var read = 0; while (read < length) { - var n = await _stream.ReadAsync(destination, offset + read, length - read, ct).ConfigureAwait(false); + var n = await _stream + .ReadAsync(destination, offset + read, length - read, ct) + .ConfigureAwait(false); if (n == 0) { throw new EndOfStreamException(); @@ -61,27 +88,47 @@ namespace SharpCompress.Common public void Dispose() { - if (_disposed || leaveOpen) + if (_disposed) { - _disposed = true; return; } _disposed = true; - _stream.Dispose(); + + // Dispose the buffered stream if we created it + if (_stream != _originalStream) + { + _stream.Dispose(); + } + + // Dispose the original stream if we own it + if (!_leaveOpen) + { + _originalStream.Dispose(); + } } #if NET6_0_OR_GREATER - public ValueTask DisposeAsync() + public async ValueTask DisposeAsync() { - if (_disposed || leaveOpen) + if (_disposed) { - _disposed = true; - return default; + return; } _disposed = true; - return _stream.DisposeAsync(); + + // Dispose the buffered stream if we created it + if (_stream != _originalStream) + { + await _stream.DisposeAsync().ConfigureAwait(false); + } + + // Dispose the original stream if we own it + if (!_leaveOpen) + { + await _originalStream.DisposeAsync().ConfigureAwait(false); + } } #endif }