Use BufferedStream for async reading in AsyncBinaryReader

Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-30 11:23:55 +00:00
parent af719707bf
commit 39a0b4ce78

View File

@@ -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<byte> 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<ulong> 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
}