2025-11-26 08:09:20 +00:00
|
|
|
using System;
|
|
|
|
|
using System.Buffers.Binary;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace SharpCompress.Common
|
|
|
|
|
{
|
2025-12-30 11:23:55 +00:00
|
|
|
public sealed class AsyncBinaryReader : IDisposable
|
2025-11-26 08:09:20 +00:00
|
|
|
{
|
2025-12-30 11:23:55 +00:00
|
|
|
private readonly Stream _stream;
|
|
|
|
|
private readonly Stream _originalStream;
|
|
|
|
|
private readonly bool _leaveOpen;
|
2025-11-26 08:09:20 +00:00
|
|
|
private readonly byte[] _buffer = new byte[8];
|
|
|
|
|
private bool _disposed;
|
|
|
|
|
|
2025-12-30 11:23:55 +00:00
|
|
|
public AsyncBinaryReader(Stream stream, bool leaveOpen = false, int bufferSize = 4096)
|
|
|
|
|
{
|
2026-01-20 12:34:49 +00:00
|
|
|
if (!stream.CanRead)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Stream must be readable.");
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-30 11:23:55 +00:00
|
|
|
_originalStream = stream ?? throw new ArgumentNullException(nameof(stream));
|
|
|
|
|
_leaveOpen = leaveOpen;
|
|
|
|
|
|
2026-01-02 18:10:54 +00:00
|
|
|
// Use the stream directly without wrapping in BufferedStream
|
|
|
|
|
// BufferedStream uses synchronous Read internally which doesn't work with async-only streams
|
|
|
|
|
_stream = stream;
|
2025-12-30 11:23:55 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-26 08:09:20 +00:00
|
|
|
public Stream BaseStream => _stream;
|
|
|
|
|
|
|
|
|
|
public async ValueTask<byte> ReadByteAsync(CancellationToken ct = default)
|
|
|
|
|
{
|
2026-01-03 15:58:27 +00:00
|
|
|
await _stream.ReadExactAsync(_buffer, 0, 1, ct).ConfigureAwait(false);
|
2025-11-26 08:09:20 +00:00
|
|
|
return _buffer[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async ValueTask<ushort> ReadUInt16Async(CancellationToken ct = default)
|
|
|
|
|
{
|
2026-01-03 15:58:27 +00:00
|
|
|
await _stream.ReadExactAsync(_buffer, 0, 2, ct).ConfigureAwait(false);
|
2025-11-26 08:09:20 +00:00
|
|
|
return BinaryPrimitives.ReadUInt16LittleEndian(_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async ValueTask<uint> ReadUInt32Async(CancellationToken ct = default)
|
|
|
|
|
{
|
2026-01-03 15:58:27 +00:00
|
|
|
await _stream.ReadExactAsync(_buffer, 0, 4, ct).ConfigureAwait(false);
|
2025-11-26 08:09:20 +00:00
|
|
|
return BinaryPrimitives.ReadUInt32LittleEndian(_buffer);
|
|
|
|
|
}
|
2025-12-30 11:23:55 +00:00
|
|
|
|
2025-11-26 08:09:20 +00:00
|
|
|
public async ValueTask<ulong> ReadUInt64Async(CancellationToken ct = default)
|
|
|
|
|
{
|
2026-01-03 15:58:27 +00:00
|
|
|
await _stream.ReadExactAsync(_buffer, 0, 8, ct).ConfigureAwait(false);
|
2025-11-26 08:09:20 +00:00
|
|
|
return BinaryPrimitives.ReadUInt64LittleEndian(_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-15 15:20:52 +00:00
|
|
|
public async ValueTask ReadBytesAsync(
|
|
|
|
|
byte[] bytes,
|
|
|
|
|
int offset,
|
|
|
|
|
int count,
|
|
|
|
|
CancellationToken ct = default
|
|
|
|
|
)
|
2025-11-26 08:09:20 +00:00
|
|
|
{
|
2026-01-14 15:33:08 +00:00
|
|
|
await _stream.ReadExactAsync(bytes, offset, count, ct).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async ValueTask SkipAsync(int count, CancellationToken ct = default)
|
|
|
|
|
{
|
2026-01-15 15:20:52 +00:00
|
|
|
await _stream.SkipAsync(count, ct).ConfigureAwait(false);
|
2025-11-26 08:09:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2025-12-30 11:23:55 +00:00
|
|
|
if (_disposed)
|
2025-11-26 08:09:20 +00:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_disposed = true;
|
2025-12-30 11:23:55 +00:00
|
|
|
|
|
|
|
|
// Dispose the original stream if we own it
|
|
|
|
|
if (!_leaveOpen)
|
|
|
|
|
{
|
|
|
|
|
_originalStream.Dispose();
|
|
|
|
|
}
|
2025-11-26 08:09:20 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-15 18:31:06 +00:00
|
|
|
#if NET8_0_OR_GREATER
|
2025-12-30 11:23:55 +00:00
|
|
|
public async ValueTask DisposeAsync()
|
2025-11-26 08:09:20 +00:00
|
|
|
{
|
2025-12-30 11:23:55 +00:00
|
|
|
if (_disposed)
|
2025-11-26 08:09:20 +00:00
|
|
|
{
|
2025-12-30 11:23:55 +00:00
|
|
|
return;
|
2025-11-26 08:09:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_disposed = true;
|
2025-12-30 11:23:55 +00:00
|
|
|
|
|
|
|
|
// Dispose the original stream if we own it
|
|
|
|
|
if (!_leaveOpen)
|
|
|
|
|
{
|
|
|
|
|
await _originalStream.DisposeAsync().ConfigureAwait(false);
|
|
|
|
|
}
|
2025-11-26 08:09:20 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|